Auth

GET /api/auth/login

Request example:

GET /api/auth/login HTTP/1.1
Host: localhost:8080

Response example (redirect to Auth Service):

HTTP/1.1 307 Temporary Redirect
X-Request-Id: f0b458f3-cf0e-4252-b118-bf601917bf90
X-RateLimit-Remaining: 9
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Location: http://localhost:8082/authorize?response_type=code&client_id=bff-user&redirect_uri=http://localhost:8083/api/auth/callback/testbed&scope=openid%20email%20profile&state=DBDGLhHLc3UGhiBIqqUL39n3bKZsabk_uQH92-eM8g0&nonce=j2vlztQDqdl1AOJnDVyqZPZoCPO4llwzuonNDRnS0Z4&code_challenge=-pLu2U_Dns0ulfkTiFIyHQA2-nthW3PzdjaJA0Y4P40&code_challenge_method=S256
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0
Set-Cookie: pkce_data=c8YF_CRB_y3h7-YWSR0X0UlY_8BlyZPS9Y9nxav3CQbwtPUpy-jnZJWXYgxtFS3UFTCHhv6ht9IykzMZ6wukWe63NQ25eg4fCak04y6HY_R9QtknP-_fRwNtBc8tfX5UV5KOFpcJWR3cd1XwS4wmJeFA1GHVF1HKetTLltdi28TJS8ckAFUjBr0Y0COwGv7sYQnHo4BJavOmkCrqZz78uhw-IXWoQmAukga0dc-Cws_MvjgLc6eHxNkChu_gMtsQzKm6zNiV; Path=/api/auth; Max-Age=300; Expires=Sun, 29 Mar 2026 13:14:59 GMT; HttpOnly; SameSite=Lax

POST /api/auth/logout

Request example:

POST /api/auth/logout HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

Response example:

HTTP/1.1 200 OK
X-Request-Id: 6aed29a1-c381-407d-abec-f25b9e02a37a
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 134
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0
Set-Cookie: access_token=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax
Set-Cookie: refresh_token=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax
Set-Cookie: id_token=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax

{"success":true,"endSessionUrl":"http://localhost:8082/end_session?client_id=bff-user&post_logout_redirect_uri=http://localhost:5173"}

Response fields:

Path Type Description

success

Boolean

是否成功登出

endSessionUrl

String

RP-Initiated Logout URL(Auth Service /end_session)

GET /api/auth/me

Request example:

GET /api/auth/me HTTP/1.1
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk3OTksImV4cCI6MTc3NDc4OTg1OX0.h3CGKgHVOaopAOb8DEGAYL-3sCiXPtOjZ0NBlDPMQb1ym3S4Ar7gRlGNqvKNBkY37TptiVyw6SzFfP1sFldQDA
Host: localhost:8080

Response example:

HTTP/1.1 200 OK
X-Request-Id: e0e4c56b-b7a8-465d-8923-0e2ea706f47a
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 95
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"accountId":"external-user-123","email":"test@example.com","roles":["USER"],"status":"ACTIVE"}

Response fields:

Path Type Description

accountId

String

帳號 ID

email

String

Email 地址

roles

Array

角色列表

status

String

帳號狀態(如 ACTIVE / PENDING)

User

PUT /api/users/me

Request example:

PUT /api/users/me HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk4MDAsImV4cCI6MTc3NDc4OTg2MH0.GyUlLQlSwwPz0jqPMJwoA2wla1Edzt9BVgdwzjhNbxwgZvw73S7AGCfi5Yjf9ExDbGQIyEES71vOlCoa2ldNCg
Content-Length: 82
Host: localhost:8080

{"displayName":"NewName","avatarUrl":"https://example.com/avatar.png","version":0}

Request fields:

Path Type Description

displayName

String

顯示名稱

avatarUrl

String

頭像 URL

version

Number

Optimistic Lock 版本號

Response example:

HTTP/1.1 200 OK
X-Request-Id: c30e8837-1ae5-400f-9150-e2e1223bdac0
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 114
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"accountId":"external-user-123","displayName":"NewName","avatarUrl":"https://example.com/avatar.png","version":1}

Response fields:

Path Type Description

accountId

String

帳號 ID

displayName

String

更新後的顯示名稱

avatarUrl

String

更新後的頭像 URL

version

Number

更新後的版本號

Notifications

GET /api/notifications

Request example:

GET /api/notifications?pageSize=20&unreadOnly=false HTTP/1.1
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk3OTgsImV4cCI6MTc3NDc4OTg1OH0.AH5fV7K-ClKXKqjLPh7CeKFNCl82lMFYNOHq-E-9jA9s6pIcAHX0Wqh_9SYjBNuqy9myR3oB4Eyh0h7YZIpasA
Host: localhost:8080

Query parameters:

Parameter Description

pageSize

每頁筆數(1~100)

unreadOnly

是否只回傳未讀通知(true/false)

Response example:

HTTP/1.1 200 OK
X-Request-Id: 43f4ba00-962e-4881-8805-4ec247e9db9f
X-RateLimit-Remaining: 57
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 312
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"notifications":[{"id":"ntf-initial","accountId":"external-user-123","type":"SYSTEM_ANNOUNCEMENT","title":"Welcome","body":"Welcome to the notification center","metadata":{},"read":true,"createdAt":"2026-03-29T13:09:51.487676060Z","readAt":"2026-03-29T13:09:57.890217160Z"}],"nextPageToken":null,"totalCount":1}

Response fields:

Path Type Description

notifications

Array

通知列表

notifications[].id

String

通知 ID

notifications[].accountId

String

帳號 ID

notifications[].type

String

通知類型

notifications[].title

String

通知標題

notifications[].body

String

通知內容

notifications[].metadata

Object

額外資訊

notifications[].read

Boolean

是否已讀

notifications[].createdAt

String

建立時間(ISO-8601)

notifications[].readAt

String

已讀時間(ISO-8601)

nextPageToken

String

下一頁游標

totalCount

Number

符合條件的總筆數

GET /api/notifications/unread-count

Request example:

GET /api/notifications/unread-count HTTP/1.1
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk3OTgsImV4cCI6MTc3NDc4OTg1OH0.686l2UOhZQZijFvYeUopNg8RaoLaPSqUAfIw-kJJpALWvD2XFAofMLHTfjoo0kauoAcGYb9kFdYvPWEKXOHVrg
Host: localhost:8080

Response example:

HTTP/1.1 200 OK
X-Request-Id: 1cef5ac7-be4b-4508-b527-ac20cd9c20ae
X-RateLimit-Remaining: 58
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 11
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"count":0}

Response fields:

Path Type Description

count

Number

未讀通知數

PATCH /api/notifications/{id}/read

Request example:

PATCH /api/notifications/ntf-initial/read HTTP/1.1
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk3OTgsImV4cCI6MTc3NDc4OTg1OH0.dpLHPRZ14PXsGAu6YqBYs1_HumR3-w_b5HUowKEX9fPDS5Q2ZjaCOVZqlaRCR8GNodpOfk5eTFvaU7BLCZB4nw
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

Path parameters: ./api/notifications/{id}/read

Parameter Description

id

通知 ID

Response example:

HTTP/1.1 204 No Content
X-Request-Id: 04163af7-9e85-4cc4-89c5-78a1e1dd14b3
X-RateLimit-Remaining: 56
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

PATCH /api/notifications/read-all

Request example:

PATCH /api/notifications/read-all HTTP/1.1
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiVVNFUiJdLCJzdGF0dXMiOiJBQ1RJVkUiLCJpYXQiOjE3NzQ3ODk3OTcsImV4cCI6MTc3NDc4OTg1N30.Y--gWpYkm9hqATv3CwaODA_3t1SESxGyomGPWmJK-ur8yumVwPdndq3nbctLmNcrpLLlqfuKjsvJA9_jjvwDpQ
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

Response example:

HTTP/1.1 200 OK
X-Request-Id: 95d0cada-739e-4431-b52b-a8537b460fe2
X-RateLimit-Remaining: 59
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 17
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"markedCount":1}

Response fields:

Path Type Description

markedCount

Number

本次標記為已讀的通知數

POST /api/admin/notifications/broadcast

Request example:

POST /api/admin/notifications/broadcast HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJraWQiOiJ0ZXN0LWtleS0xIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJleHRlcm5hbC11c2VyLTEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC51Zm90ZWMuY29tIiwiYXVkIjpbImh0dHBzOi8vYXBpLnVmb3RlYy5jb20iXSwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwicm9sZXMiOlsiQURNSU4iXSwic3RhdHVzIjoiQUNUSVZFIiwiaWF0IjoxNzc0Nzg5Nzk4LCJleHAiOjE3NzQ3ODk4NTh9.hZx4YPRjR4MR9MBuOCeXoRUiJw8hiwK8K8DjNR0_b21_XH_4JHN45HsDUi4LtpkGuyju5nvuJGCva4qcC7p7aQ
Content-Length: 143
Host: localhost:8080

{"type":"SYSTEM_ANNOUNCEMENT","title":"Maintenance","body":"Planned maintenance","metadata":{"scope":"all"},"accountIds":["external-user-123"]}

Request fields:

Path Type Description

type

String

通知類型

title

String

通知標題

body

String

通知內容

metadata

Object

額外資訊(可自由擴充 key/value)

accountIds

Array

目標帳號清單,不可為空(broadcast-to-all 尚未支援)

Response example:

HTTP/1.1 200 OK
X-Request-Id: c4d57814-3073-4ebb-99e9-756b416f3086
X-RateLimit-Remaining: 8
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 18
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 0

{"createdCount":1}

Response fields:

Path Type Description

createdCount

Number

建立的通知筆數