{
  "openapi": "3.1.0",
  "info": {
    "title": "鳥哥講投資 Public API",
    "version": "1.0.0",
    "description": "鳥哥講投資 (birdlovestrade.com) 官方公開 API。提供美股觀點早報訂閱 (/api/subscribe) 與電子報取消訂閱 (/api/unsubscribe) 介面。代理必須先取得使用者明確同意，才可提交訂閱或退訂。版本策略：本 API 遵循語意化版本（SemVer 2.0.0），向後相容變更保留在 /api 路徑；重大變更將使用 URL 路徑版本 (/api/v1/) 與 Sunset 標頭公告廢棄期。"
  },
  "servers": [
    {
      "url": "https://birdlovestrade.com",
      "description": "正式站 (Production)"
    }
  ],
  "paths": {
    "/api/subscribe": {
      "post": {
        "operationId": "subscribeNewsletter",
        "summary": "訂閱美股觀點早報",
        "description": "取得使用者明確同意後，提交 Email 訂閱鳥哥講投資美股觀點早報。正式站必須提供由瀏覽器取得的有效 Cloudflare Turnstile token，不可繞過驗證。支援 application/x-www-form-urlencoded、multipart/form-data 與 application/json 格式。驗證通過後將加入郵件名單並透過 302 重定向至感謝頁面；發生錯誤時回傳結構化 JSON 錯誤回應。",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            }
          }
        },
        "responses": {
          "302": {
            "description": "訂閱成功或重複訂閱，重定向至感謝頁面 (/thanks-us-brief)",
            "headers": {
              "Location": {
                "description": "重定向目的地網址，例如 /thanks-us-brief?sub=ok&mail=accepted 或 /thanks-us-brief?sub=ok-dup",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "請求格式錯誤或 Email 格式無效",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "invalidEmail": {
                    "summary": "無效的 Email",
                    "value": {
                      "code": "invalid_email",
                      "message": "Email 格式不正確，請重新輸入。",
                      "hint": "請輸入有效的電子郵件地址（例如 user@example.com）。"
                    }
                  },
                  "invalidForm": {
                    "summary": "請求 Body 格式錯誤",
                    "value": {
                      "code": "bad_request",
                      "message": "表單格式不正確，請重新送出。",
                      "hint": "請以 application/x-www-form-urlencoded、multipart/form-data 或 application/json 格式送出請求。"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Cloudflare Turnstile 人機驗證失敗",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "code": "turnstile_verification_failed",
                  "message": "人機驗證失敗，請重新送出。",
                  "hint": "請提供有效的 Cloudflare Turnstile token（cf-turnstile-response）。"
                }
              }
            }
          },
          "405": {
            "description": "不支援的 HTTP 方法（僅支援 POST）",
            "headers": {
              "Allow": {
                "description": "允許的 HTTP 方法",
                "schema": {
                  "type": "string",
                  "example": "POST"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "code": "method_not_allowed",
                  "message": "只接受 POST 表單送出。",
                  "hint": "請改用 POST 方法發送請求。"
                }
              }
            }
          },
          "502": {
            "description": "下游郵件發送服務（Resend/Moosend）連線或寫入失敗",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "code": "provider_error",
                  "message": "訂閱寫入失敗，請稍後再試。",
                  "hint": "後端郵件服務暫時無法完成寫入，請稍後重試。"
                }
              }
            }
          },
          "503": {
            "description": "系統維護中、名單容量達到上限或伺服器設定不完整",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "maintenance": {
                    "summary": "系統維護中",
                    "value": {
                      "code": "service_maintenance",
                      "message": "訂閱系統維護中，請稍後再試。",
                      "hint": "系統目前正在維護中，請稍候再試。"
                    }
                  },
                  "capacity": {
                    "summary": "名單容量達到上限",
                    "value": {
                      "code": "capacity_reached",
                      "message": "訂閱系統暫時無法受理，請稍後再試。",
                      "hint": "名單容量暫時達到上限，請稍後重試或聯繫站長。"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/unsubscribe": {
      "get": {
        "operationId": "getUnsubscribeConfirmation",
        "summary": "取得取消訂閱確認頁面",
        "description": "驗證電子郵件退訂連結中帶入的聯絡人 ID 與 HMAC-SHA256 簽章。驗證成功時回傳 HTML 確認頁面（含退訂 POST 表單）；若簽章無效或聯絡人格式不正確，回傳 404 Not Found。",
        "parameters": [
          {
            "name": "c",
            "in": "query",
            "required": true,
            "description": "Resend 聯絡人 UUID",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-1111-4111-8111-111111111111"
            }
          },
          {
            "name": "s",
            "in": "query",
            "required": true,
            "description": "HMAC-SHA256 base64url 安全簽章",
            "schema": {
              "type": "string",
              "example": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "簽章驗證通過，回傳取消訂閱 HTML 確認表單",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "聯絡人 ID 格式不符、簽章無效或驗證未通過（統一回傳 generic Not found 防止探測）",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Not found"
                }
              }
            }
          },
          "405": {
            "description": "不支援的 HTTP 方法"
          }
        }
      },
      "post": {
        "operationId": "confirmUnsubscribe",
        "summary": "執行取消訂閱",
        "description": "接收退訂確認表單送出的 POST 請求。限制同源請求 (same-origin) 與表單 Content-Type，驗證簽章後將聯絡人設定為 unsubscribed 並 302 重定向至 /unsubscribed 頁面。",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/UnsubscribeConfirmRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UnsubscribeConfirmRequest"
              }
            }
          }
        },
        "responses": {
          "302": {
            "description": "取消訂閱成功，重定向至 /unsubscribed",
            "headers": {
              "Location": {
                "description": "重定向目的地網址",
                "schema": {
                  "type": "string",
                  "example": "https://birdlovestrade.com/unsubscribed"
                }
              }
            }
          },
          "400": {
            "description": "跨來源請求 (Origin mismatch) 或 Content-Type 不符表單規範",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Bad request"
                }
              }
            }
          },
          "404": {
            "description": "聯絡人 ID 格式不符、簽章無效或聯絡人不存在",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Not found"
                }
              }
            }
          },
          "405": {
            "description": "不支援的 HTTP 方法"
          },
          "503": {
            "description": "後端服務暫時無法連線",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Temporarily unavailable"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SubscribeRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "訂閱者的電子郵件地址",
            "example": "reader@example.com"
          },
          "cf-turnstile-response": {
            "type": "string",
            "description": "Cloudflare Turnstile 人機驗證 token（正式環境必填）",
            "example": "0.xxxxxx.yyyyyy"
          }
        }
      },
      "UnsubscribeConfirmRequest": {
        "type": "object",
        "required": [
          "c",
          "s"
        ],
        "properties": {
          "c": {
            "type": "string",
            "format": "uuid",
            "description": "Resend 聯絡人 UUID",
            "example": "11111111-1111-4111-8111-111111111111"
          },
          "s": {
            "type": "string",
            "description": "HMAC-SHA256 base64url 安全簽章",
            "example": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "code",
          "message",
          "hint"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "機器可識別的穩定錯誤代碼",
            "example": "invalid_email"
          },
          "message": {
            "type": "string",
            "description": "人類可讀的錯誤說明",
            "example": "Email 格式不正確，請重新輸入。"
          },
          "hint": {
            "type": "string",
            "description": "給用戶或客戶端的修復與重試提示",
            "example": "請輸入有效的電子郵件地址（例如 user@example.com）。"
          }
        }
      }
    }
  }
}
