mass_mail_engine/docs/OPENAPI.md
warrenchen e9712fb1f7 feat: Implement SendEngine database context and migrations
- Added SendEngineDbContext for managing database interactions.
- Created SendEngineDbContextFactory for design-time database context creation.
- Established dependency injection for the infrastructure layer.
- Defined entity configurations for Tenant, MailingList, Subscriber, ListMember, EventInbox, Campaign, SendJob, SendBatch, DeliverySummary, AuthClient, AuthClientKey, and WebhookNonce.
- Generated initial database migration snapshot.
- Implemented installer program for database migration commands.
2026-02-10 17:56:29 +09:00

5.5 KiB
Raw Blame History

OpenAPI Notes

本文件描述 Send Engine 對外 API、Webhook 驗證與與 Member Center 的介接規則。 目標是讓 Member Center 與租戶網站可以清楚交換資料與責任邊界。

Auth 與驗證

1. 租戶網站 → Send Engine API

使用 OAuth2 Client Credentials 或 JWT由 Member Center 簽發)。

必要 claims

  • tenant_id
  • scope(至少 newsletter:send.write

規則:

  • tenant_id 只能取自 token不接受 body 覆寫
  • list_id 必須屬於該 tenant

2. Member Center → Send Engine Webhook

使用簽名 WebhookHMAC或 OAuth2 Client Credentials建議簽名

Header 建議:

  • X-Signature: hex(hmac_sha256(secret, body))
  • X-Timestamp: Unix epoch seconds
  • X-Nonce: UUID
  • X-Client-Id: Auth client UUID對應 auth_clients.id

驗證規則:

  • timestamp 在允許時間窗內(例如 ±5 分鐘)
  • nonce 不可重複(重放防護)
  • signature 必須匹配
  • client 必須存在且為 active

3. Send Engine → Member Center 回寫

使用 OAuth2 Client CredentialsSend Engine 作為 client

scope 最小化:

  • newsletter:events.write(停用回寫)
  • newsletter:list.read(若未來仍需查詢)

通用欄位

Timestamp

  • 欄位:occurred_at
  • 格式RFC3339例如 2026-02-10T09:30:00Z

ID

  • tenant_idlist_idsubscriber_idevent_id 皆為 UUID

通用錯誤格式

{
  "error": "string_code",
  "message": "human readable message",
  "request_id": "uuid"
}

WebhookMember Center → Send Engine

A. 訂閱事件(增量)

用途:同步新增/取消/偏好變更。

Endpoint

  • POST /webhooks/subscriptions

Scope

  • newsletter:events.write

事件型別:

  • subscription.activated
  • subscription.unsubscribed
  • preferences.updated

Request Body示意

{
  "event_id": "uuid",
  "event_type": "subscription.activated",
  "tenant_id": "uuid",
  "list_id": "uuid",
  "subscriber": {
    "id": "uuid",
    "email": "user@example.com",
    "status": "active",
    "preferences": { "topic": "news" }
  },
  "occurred_at": "2026-02-10T09:30:00Z"
}

Response

  • 200 OKaccepted
  • 401/403:驗證失敗
  • 409event_id 重複或 nonce 重放
  • 422:格式錯誤

B. 全量名單同步

用途:由 Member Center 主動推送全量或分批名單,避免 Send Engine 拉取名單。

Endpoint

  • POST /webhooks/lists/full-sync

Scope

  • newsletter:events.write

Request Body分批示意

{
  "sync_id": "uuid",
  "batch_no": 1,
  "batch_total": 10,
  "tenant_id": "uuid",
  "list_id": "uuid",
  "subscribers": [
    { "id": "uuid", "email": "a@example.com", "status": "active" },
    { "id": "uuid", "email": "b@example.com", "status": "unsubscribed" }
  ],
  "occurred_at": "2026-02-10T09:30:00Z"
}

Response

  • 200 OKaccepted
  • 401/403:驗證失敗
  • 409sync_id + batch_no 重複
  • 422:格式錯誤

API租戶網站 → Send Engine

C. 建立 Send Job

用途:租戶網站送入內容,建立 Campaign/Send Job 並排程。

Endpoint

  • POST /api/send-jobs

Scope

  • newsletter:send.write

Request Body

{
  "tenant_id": "uuid",
  "list_id": "uuid",
  "name": "Weekly Update",
  "subject": "Weekly Update",
  "body_html": "<p>Hello</p>",
  "body_text": "Hello",
  "template": null,
  "scheduled_at": "2026-02-11T02:00:00Z",
  "window_start": "2026-02-11T02:00:00Z",
  "window_end": "2026-02-11T05:00:00Z",
  "tracking": { "open": true, "click": false }
}

欄位規則:

  • subject 必填,最小長度 1
  • body_html / body_text / template 至少擇一
  • window_start 必須小於 window_end(若有提供)

Response

{
  "send_job_id": "uuid",
  "status": "pending"
}

D. 查詢 Send Job

Endpoint

  • GET /api/send-jobs/{id}

Scope

  • newsletter:send.read

Response

{
  "id": "uuid",
  "tenant_id": "uuid",
  "list_id": "uuid",
  "campaign_id": "uuid",
  "status": "running",
  "scheduled_at": "2026-02-11T02:00:00Z",
  "window_start": "2026-02-11T02:00:00Z",
  "window_end": "2026-02-11T05:00:00Z"
}

E. 取消 Send Job

Endpoint

  • POST /api/send-jobs/{id}/cancel

Scope

  • newsletter:send.write

Response

{
  "id": "uuid",
  "status": "cancelled"
}

WebhookSES → Send Engine

F. SES 事件回報

用途:接收 bounce/complaint/delivery/open/click 等事件。

Endpoint

  • POST /webhooks/ses

驗證:

  • 依 SES/SNS 規格驗簽(可用 Ses__SkipSignatureValidation=true 暫時略過)

Request Body示意

{
  "event_type": "bounce",
  "message_id": "ses-id",
  "tenant_id": "uuid",
  "email": "user@example.com",
  "bounce_type": "hard",
  "occurred_at": "2026-02-10T09:45:00Z"
}

Response

  • 200 OK

外部 APISend Engine → Member Center

以下為 Member Center 端提供的 API非 Send Engine 的 OpenAPI 規格範圍。

G. 停用訂閱回寫

用途:因 hard bounce / complaint 停用訂閱,並在 Member Center 註記來源。

EndpointMember Center 提供):

  • POST /api/subscriptions/disable

Scope

  • newsletter:events.write

Request Body示意

{
  "tenant_id": "uuid",
  "subscriber_id": "uuid",
  "list_id": "uuid",
  "reason": "hard_bounce",
  "disabled_by": "send_engine",
  "occurred_at": "2026-02-10T09:45:00Z"
}

狀態碼與錯誤

通用錯誤:

  • 401/403Auth 或 scope 不符
  • 409重放或事件重複nonce / event_id
  • 422:資料格式錯誤
  • 500:伺服器內部錯誤