HTTP API 參考

語言與錯誤碼

英文是預設語言。API、Web 控制台和通知渲染共用 internal/i18n/locales/ 下的內嵌文案,internal/i18n/languages.json 定義語言程式碼和本機名稱。

程式碼 語言
en English
zh-CN 简体中文
zh-Hant 繁體中文
ja 日本語
ko 한국어
de Deutsch
fr Français
es Español
pt-BR Português (Brasil)
  • 首次存取按瀏覽器的 Accept-Language 選擇語言,切換後在本機儲存規範程式碼。控制台語言與已儲存的通知語言獨立。
  • API(包括 App 管理介面)按 Accept-Language 權重協商,返回 Content-Language 和 Vary: Accept-Language。跳過不支援的偏好;預設、非法或全部不支援時回退 en,權重 0 的語言不參與協商。
  • 地區別名對映到對應語言:fr-CA → fr,es-MX → es,pt / pt-PT → pt-BR。中文區分字形:zh-Hans / zh-CN / zh-SG → zh-CN;zh-Hant / zh-TW / zh-HK / zh-MO → zh-Hant。顯式字形優先於地區,單獨 zh 使用簡體。
  • GET /locales/default.json 返回協商後的文案;GET /locales/{规范代码}.json 返回指定文案;未知文案路徑返回 locale_not_supported。
  • 通道設定 language 獨立控制哨站生成的通知,支援上述規範程式碼及地區別名,儲存時歸一化;省略或空值使用 en,顯式不支援的值返回 400 config_language_invalid。請求語言不會覆蓋該設定。
  • Native 測試文案由推播中繼服務根據配對裝置語言生成,哨站通知語言不會覆蓋它。推播中繼服務和 App 各自支援相應的語言。
  • 控制台本機化標準資料夾角色及常見名稱。API 保留原始 IMAP 路徑、自定義名稱和 folder_roles,App 使用自己的文案翻譯角色。通知標題繼續使用原始資料夾末級名稱;使用者輸入、郵件主旨、內文和驗證碼保持原值。
  • 日誌和 CLI 使用英文。業務錯誤保持穩定的英文 snake_case 錯誤碼與命名引數;儲存保留程式碼和引數,展示層負責翻譯。API 時間和數字保持原有機器可讀格式,控制台按所選語言格式化時間。
json
{
  "error": {
    "code": "unauthorized",
    "message": "请使用有效会话或 API Token 登录。"
  }
}

投遞記錄的 last_error 與資料夾狀態的 notice 使用相同的 {code, message, params?} 結構,例如 bark_http_error 搭配 {"status":"503"}。按 code 判斷分支,message 用於展示。

每個信箱的獨立連線預算內預留 1 個名額給資料夾發現與連線測試。監聽需求按“實時 Folder 數 +(存在定時 Folder 時的 1 個)”計算,最多使用 connection_limit - 1 個名額(預設 9 個);該信箱所有操作的總連線數保持在 connection_limit(2–100)以內。已儲存上限為 1 的設定按 configuration_invalid 降級啟動,允許透過該信箱的 PUT 介面修正。

HTTP API

GET /、靜態資源和 /healthz 公開存取。首次設定完成前,其他管理介面統一返回 403 setup_required;POST /api/v1/setup 只能成功一次。設定後公開登入介面 POST /api/v1/session,其餘介面使用會話 Cookie 或 Authorization: Bearer mwk_…。

Cookie 屬性為 HttpOnly、SameSite=Strict、Path=/,TLS 連線或 HTTPS Origin 加 Secure;空閒 7 天或絕對 30 天過期。Cookie 修改請求同時要求 X-CSRF-Token 和使用 HTTP 或 HTTPS、主機與埠和請求 Host 完全一致的 Origin。Bearer 請求免於 CSRF;顯式提交錯誤的 Authorization 時,即使 Cookie 有效也拒絕請求。

登入和設定碼失敗按連線來源地址共享限額:15 分鐘內 10 次失敗,後續請求返回 429 too_many_attempts,Retry-After 為秒數。密碼修改也使用該限額,來源由 Gin ClientIP 按上述可信代理網段解析。登入和設定請求中的跨站 Origin 會被拒絕。由於所有內網對端都被視為可信代理,區域網內的客戶端可以偽造 X-Forwarded-For,把失敗嘗試分散到多個來源地址。不要把哨站監聽埠暴露在不可信的區域網中,只透過反向代理對外提供存取。

JSON 請求體上限 64 KiB,未知欄位或多餘 JSON 返回 400。密碼至少 12 個字元、最多 1024 個 UTF-8 位元組;使用者名稱及 Token 名稱為 1–128 位元組。管理 API 的時間戳統一為 UTC RFC 3339 字串,包括投遞 created_at/accepted_at、Token created_at/last_used_at、資料夾檢查時間、日誌時間和診斷生成時間;小數秒保留儲存精度。未使用 Token 的 last_used_at 為 null,尚未接受的投遞省略 accepted_at。計數、時長和 revision 保持數字。

401 表示會話或 API Token 鑑權失敗,或 POST /api/v1/session 登入憑據錯誤。設定碼錯誤返回 400 setup_code_invalid,嚮導回到設定碼步驟並保留使用者名稱。PUT /api/v1/admin/password 的當前密碼錯誤返回 400 current_password_invalid,仍計入失敗限速,當前會話保持有效。登入失敗與當前密碼錯誤在原表單內提示並保留輸入。

方法 路徑 請求與回應
POST /api/v1/setup {"code":"XXXX-XXXX-XXXX","username":"admin","password":"…"};201,Cookie 與 csrf_token;重複設定返回 409
POST /api/v1/session {"username":"admin","password":"…"};200,Cookie 與 csrf_token
GET /api/v1/session 返回 authenticated:true、csrf_token;Bearer 返回 method:"token"
DELETE /api/v1/session 204,登出當前 Cookie 會話;需要會話鑑權
PUT /api/v1/admin/password {"current_password":"…","password":"…"};204,登出其他會話;Bearer 修改會登出全部會話
POST /api/v1/tokens {"name":"automation"};201,返回 id、name、created_at、last_used_at 和僅顯示一次的 token
GET /api/v1/tokens {"tokens":[…]},僅後設資料
DELETE /api/v1/tokens/:id 204,撤銷 Token;重複撤銷仍返回 204
GET /api/v1/mailboxes {"mailboxes":[…]},信箱脫敏檢視列表
POST /api/v1/mailboxes 使用下方信箱表單建立;201,返回生成的 ID、revision 1 和脫敏檢視
GET / PUT / DELETE /api/v1/mailboxes/:id 讀取 / 攜帶當前 revision 更新 / 刪除(204);不存在返回 404 mailbox_not_found
POST /api/v1/mailboxes/test 測試未儲存的信箱表單,返回 {"folders":[…]},保持設定原值
POST /api/v1/mailboxes/:id/test 合併該信箱已儲存的憑據後測試,連線目標相同時省略密碼保留原值
GET /api/v1/mailboxes/:id/folders 使用該信箱預算發現可選資料夾
GET / PUT /api/v1/mailboxes/:id/subscriptions 讀取 / 按獨立 revision 儲存訂閱;成功返回 202
GET / PUT /api/v1/settings/delivery 讀取 / 攜帶獨立 revision 儲存全域性通道設定
DELETE /api/v1/settings/delivery 攜帶 {revision} 清除通道與憑據;204,版本衝突409
POST /api/v1/settings/delivery/test 合併已儲存憑據並測試通道表單;204,保持設定原值
GET /api/v1/status 資料夾狀態、通道、投遞計數及按作用域組織的 notices
GET /api/v1/diagnostics 下載脫敏 JSON 診斷報告,省略執行日誌
GET /api/v1/logs 鑑權後查詢執行日誌,支援 after、level、mailbox_id、limit(見下文)
GET /api/v1/deliveries 最近 50 條請求記錄,包含標題和信箱 / 資料夾摘要
POST /api/v1/test-push 使用已儲存設定傳送測試,接受後返回 204,保持設定原值
POST /api/v1/deliveries/:id/retry 重新處理 dead 任務,返回 202

投遞記錄的可選 message 物件包含 mailbox_id、mailbox_label、folder、subject(字串或 null)和 test(布林值)。名稱按入隊時儲存,信箱改名或刪除後保持歷史來源。subject 為 null 表示標題未保留,空字串表示郵件無主旨。測試通知的 test=true,來源為空。摘要介面省略寄件者和內文。

建立信箱示例:

json
{
  "label": "工作邮箱",
  "host": "imap.example.org",
  "port": 993,
  "username": "user@example.org",
  "password": "…",
  "connection_limit": 10
}

最多儲存 20 個信箱,包含待修正的無效設定;繼續建立返回 400 mailbox_limit_exceeded。ID 為 mbx_ 加隨機串,建立後保持穩定。label 預設取 username(信箱地址),最長 64 個 Unicode 字元,禁止換行。使用者名稱超過 64 字元時需顯式填寫較短名稱。更新時省略名稱保留原值,空名稱使用本次提交的 username。

信箱 PUT 使用相同表單並攜帶 GET 讀到的 revision,例如 "revision":1,必須填寫 host、port、username。host、port、username 保持原值時,省略 password 保留原密碼,顯式空密碼會被拒絕。省略 connection_limit 保留當前值;新信箱預設 10,範圍 2–100。建立 POST 省略 revision 或傳 0。測試介面接受相同欄位,無須 revision,保持設定原值;未儲存信箱的測試需要憑據,帶 ID 的測試只合並該信箱的已有憑據。

信箱 GET 返回 id、label、revision、host、port、username、connection_limit、connections_in_use 與 "password":{"configured":true}。降級的無效信箱返回 configured:false,保留 ID 和 revision 供修正。讀取回應中的密碼物件屬於展示檢視,不能作為寫入值。

每個信箱的連線預算獨立。實時 Folder 各佔一條連線,全部定時 Folder 合計佔一條,需求最多為 connection_limit - 1,預留一條供管理。PUT 訂閱及修改連線上限共用此公式,容量不足返回 connection_budget_exceeded。未儲存信箱的測試使用臨時預算。未儲存信箱測試、已儲存信箱測試和資料夾發現三個入口還共用全域性併發上限 2,超出立即返回 429 discovery_busy,結束或取消時釋放名額。

首次儲存通道示例:

json
{
  "revision": 0,
  "channel": "bark",
  "preview": "off",
  "retry_count": 0,
  "language": "zh-CN",
  "bark": { "endpoint": "https://api.day.app", "key": "…" }
}

每次更新前讀取 /settings/delivery 並提交其 revision,首次儲存從 0 增加到 1。Pushover 使用 pushover.token、pushover.user;Webhook 使用 webhook.url、webhook.secret。channel、preview、revision 必填;省略 language、retry_count 使用預設值。省略憑據(含 Webhook URL)保留已儲存值,顯式空的當前通道憑據會被拒絕;GET 將憑據替換為 {"configured":true|false}。獨立測試介面無須 revision。

信箱建立和連線設定修改在儲存前測試連線及登入。host、port、username、connection_limit 與現值相同且省略 password 時,PUT 只改 label:仍校驗 revision 並儲存,保持連線與進度,已入隊通知保留原名稱,之後入隊使用新名稱。建立或修改後的身分若與其他信箱相同,返回 409 mailbox_duplicate;host 與 username 忽略大小寫、port 精確匹配,同身分併發建立只成功一個。通道 PUT 對所有通道統一校驗欄位、地址、所選配對狀態與 revision,直接儲存,儲存過程不傳送測試或聯絡通道服務。顯式通道測試保持設定原值,失敗後仍可儲存合法設定。信箱網路測試在設定鎖外執行,現有監聽保持執行,信箱測試失敗保留原設定。設定 revision 過期返回 409 settings_conflict;信箱測試期間訂閱變化同樣使快照失效,需重新讀取後提交。各信箱、全域性通道分別維護 revision,修改不同信箱、同時修改信箱和通道互不衝突。

PUT 成功返回脫敏檢視並立即生效。信箱連線設定更新先停止自身監聽,再儲存並按需清理進度;儲存失敗恢復原監聽,其他信箱繼續執行。通道變更時,進行中的請求沿用原快照,後續嘗試使用新快照;尚未設定通道時佇列保持待處理。

GET /api/v1/mailboxes/:id/subscriptions 返回 {"revision":1,"folders":[{"name":"INBOX","check":"realtime"},{"name":"Clients","check":"5m"}]}。PUT 提交讀取到的 revision 與完整 folders 陣列,空陣列取消該信箱的全部訂閱。名稱在信箱內唯一,INBOX 大小寫統一,每項最多 4096 個 UTF-8 位元組;每項 name、check 必填,check 為 realtime、5m 或 15m;缺失、非法 check 或舊字串陣列返回 400。連線需求使用上文公式。只改 check 保留進度和事件 ID,銜接新郵件補查;取消後重新訂閱繼續重建基線。儲存成功返回 202 和下一個版本,舊版本返回 409 subscriptions_conflict。資料夾存在性由監聽確認,信箱離線時仍可取消訂閱。儲存失敗返回 503,當前訂閱保持不變。

/status.folders[] 每項包含 mailbox_id、mailbox_label 與 check。mode 為 idle、poll 或 scheduled;定時 Folder 另有 RFC 3339 時間 next_check(失敗時為下次重試時間),實時 Folder 省略該欄位。已儲存設定的 JSON 或模型校驗失敗時,對應項降級為未設定,在 /status.notices 的 mailbox:<id> 或 delivery 下報告 configuration_invalid,可透過該項 PUT 修正。訂閱超預算時僅暫停該信箱,在 subscriptions:<id> 下報告 connection_budget_exceeded;減少訂閱或提高預算後恢復。解密失敗仍拒絕啟動。

診斷白名單包含生成時間、構建/Go/平臺資訊、佇列計數以及匿名信箱和監聽狀態(含 check 與 mode)。mailboxes[].index 與 watches[].mailbox_index 對應本次報告的“信箱 1、信箱 2……”;信箱內監聽有獨立的 index。信箱條目包含設定及訂閱 notice 的錯誤碼。僅 imap_folder_rejected 可匯出 watches[].response_code。報告省略信箱 ID、顯示名稱、地址、主機、資料夾名、郵件內容、任務 ID、憑據及其他錯誤引數。

任務狀態:pending 等待請求,accepted 通道接受請求,dead 本輪請求結束且記錄失敗。

執行日誌

透過 docker compose logs -f core(或 docker logs <容器名>)檢視 JSON stderr 日誌。管理客戶端可使用會話 Cookie 或 API Token 查詢同一組執行事件:GET /api/v1/logs?after=0&level=info&mailbox_id=mbx_example&limit=200。

記憶體環形緩衝保留最近 2000 條 Info/Warn/Error 日誌,重啟清空。after 是排除本身的非負序號;level 為 info/warn/error,包含指定級別及以上,預設 info;省略 mailbox_id 時包含全部信箱和全域性操作。limit 預設 200,範圍 1–500。引數非法返回 400 logs_request_invalid。

json
{
  "entries": [
    {
      "seq": 42,
      "time": "2026-09-29T00:00:00Z",
      "level": "info",
      "message": "Folder connection established",
      "attrs": {
        "mailbox_id": "mbx_example",
        "folder": "INBOX",
        "mode": "idle",
        "check": "realtime"
      }
    }
  ],
  "next": 42,
  "cleared_through": 0
}

下一次請求把 next 作為 after。達到頁大小時 next 為最後返回序號;遍歷完快照時為快照最大序號,過濾結果為空也推進。淘汰的日誌從當前保留記錄繼續;重啟導致 next 小於 after 時,將遊標重置為 0。條目按序號升序返回。

日誌包含信箱/Folder 生命週期、基線與入隊數量、投遞嘗試、設定變更和管理員操作,全域性事件的 mailbox_id 為空。attrs 僅輸出這些標量欄位:mailbox_id、folder、mode、check、code、event_id、channel、attempt、duration_ms、http_status、next_attempt、backoff_seconds、count、revision、token_id、version、listen。http_status=0 表示未收到 HTTP 回應;token_id 為公開記錄 ID,區別於 Token 完整值。密碼、Token/會話值、設定碼、寄件者、主旨、內文和憑據 URL 均省略;設定碼那一行只輸出到控制台。日誌包含 Folder 名稱,匿名診斷匯出繼續省略日誌和名稱。

資料夾掃描與管理員信箱測試返回原始路徑陣列 folders,並可附帶 folder_roles:路徑到 inbox、drafts、sent、trash、junk、archive、all 或 flagged 的對映。用途來自 IMAP SPECIAL-USE 屬性,INBOX 按協議名稱識別。Web 介面在缺少用途時,按常見英文資料夾名稱進行忽略大小寫的完整匹配;譯名後用括號保留原始路徑,其餘名稱保持原樣。介面翻譯不改變訂閱路徑。 通知標題僅顯示斜槓分隔路徑的末級名稱;結構化 folder 欄位保留完整路徑。App 信箱測試繼續僅返回連線狀態。

GET /api/v1/session 在 Cookie 會話回應中返回 username,Token 鑑權回應保持原有結構。

App 接收裝置

通知設定增加 native_pairing_id。native 管道必須從 /api/v1/native/devices 選擇一個有效配對;測試通知和新事件只投遞給該裝置。更新時省略此欄位保留原選擇;空選擇返回 native_target_required,失效目標返回 native_target_unavailable。當前 native 目標撤銷後,通道與 native_pairing_id 同時清空,revision 增加一次;其他目標和通道保留。已開始的投遞保留原目標及撤銷結果。狀態同步、併發與重啟規則見配對撤銷同步。

App 授權頁分別管理管理權限和推播權限,兩者獨立撤銷。

Native 測試通知

哨站使用簽章 POST /v1/pairing-tests,內文 {pairing_id,test_id};每次顯式測試生成新的 UUID test_id。推播中繼服務生成固定內容並同步提交 APNs,無須 Native Push 權益。表單測試、App 已儲存設定測試和 /api/v1/test-push 共用此呼叫鏈。測試獨立於郵件 Outbox,不自動重試。

僅推播中繼服務返回 accepted 對應哨站 HTTP 204。APNs 拒絕、結果未知、sending 均返回本機化錯誤;推播中繼服務限頻對映為哨站 HTTP 429 並保留 Retry-After。安全日誌記錄推播中繼服務 HTTP 狀態與穩定錯誤碼,省去回應內文、Token 和通知內容。哨站與 App 共享推播中繼服務現有每裝置每分鐘、每主體每日額度及 (device_id,test_id) 冪等。使用者再次點選測試會建立新的測試 ID。

正式郵件仍使用 /v1/push 並校驗權益,事件 ID 或客戶端測試標誌均保持此規則。先部署支援配對測試介面的推播中繼服務,再部署此版本哨站;哨站不回退到正式郵件介面。

清除投遞記錄與執行日誌

  • DELETE /api/v1/deliveries 返回 200 {"deleted":N},永久清除全部歷史中已結束的 accepted / dead 記錄及關聯 Native 記錄。待投遞/重試、Native 的 pending/queued/sending 狀態、待執行的狀態檢查和活動更新會保留。清除失敗記錄後,其手動重試入口一併消失。信箱檢查點與設定保持原樣。
  • DELETE /api/v1/logs 返回 200 {"cleared_through":N},清除所有級別、所有信箱共享的記憶體日誌。序號繼續遞增,標準錯誤/容器日誌和匯出檔案保留。GET /api/v1/logs 同時返回 cleared_through;客戶端先移除快取中 seq <= cleared_through 的條目,再追加回應中的日誌。
  • 兩個介面均要求管理員會話及 Origin/CSRF,或管理員 API Token。App 裝置憑證無法執行這些操作。重複清除安全;操作完成後仍可產生新任務與新日誌。

App 郵件正文

POST /api/v1/app/content 使用裝置綁定的控制憑證與站主授予的 content 權限。請求為 { "reference": <message_ref> },回應包含 text、truncated、可選的清理後 html 和 has_remote_images。正文閱讀免費。

正文可讀後,使用相同憑證、權限和引用呼叫 POST /api/v1/app/content/read,將信箱中的郵件標記為已讀。成功回傳 204 No Content 與 Cache-Control: no-store。僅取得正文會保留信箱原有的已讀狀態。兩個操作均驗證目前授權、訂閱資料夾、信箱身分和郵件引用。

憑據與連線目標

測試或儲存信箱時,修改 host、port 或 username 須重新輸入已儲存的信箱密碼,否則返回 400 mailbox_password_required_for_change。修改已儲存的 Bark 位址須重新輸入裝置 Key,即使目前選中其他通道亦適用;否則返回 400 bark_key_required_for_change。位址包含連接埠、路徑和末尾斜線。拒絕時不建立連線、不傳送通知,設定與 revision 保持原值。

App 請求診斷

診斷匯出使用 schema_version: 2,新增 app_requests:從共用的 2000 筆記憶體日誌擷取最近最多 300 筆已完成的 App 請求。每筆包含時間、伺服器產生的 request_id(同時透過 X-Request-ID 回傳)、路由範本、方法、HTTP 狀態、耗時、階段、結果與安全錯誤碼。匯出排除資源 ID、查詢參數、憑證、簽章參照、內文與完整執行日誌。重新啟動哨站或清除日誌會移除記錄,其他日誌也可能擠出較舊請求。下載前會更新報告。未到達哨站的請求仍需 App 診斷。

對於 POST /api/v1/app/content/read,哨站查詢 UID 和 flags:已讀直接成功,未讀則標記後再次確認。STORE OK 本身無法證明郵件存在。UID 不存在、UIDVALIDITY 改變或資料夾明確不存在回傳 404;服務商或權限失敗回傳 503,逾時回傳 504。遠端參照失效時,本機快取內文仍可閱讀。

Markdown · AGPL-3.0-only · Mailwake 貢獻者