台灣壽司郎 App API
本文件整理台灣壽司郎 Android App 2.2.4 使用的 CRM API。
僅供自有帳號的互通性研究與測試。這些是未公開 API,可能隨 App 或後端更新而改變。
索引
- 1. 證據與信心標示
- 2. 基本資訊
- 3. 驗證模型
- 4. 核心流程
- 5. 已確認 API
- 6. APK 內的完整端點索引
- 7. 已知錯誤
- 8. 安全與操作規則
- 9. 尚待捕獲
- 10. 工作區參考資料
1. 證據與信心標示
| 標示 | 意義 |
|---|---|
| ✅ 封包確認 | 已從官方 App 或等效請求的實際 HTTP request/response 確認 |
| 🔬 Live 測試 | 已對正式環境進行有限、可回復的自有帳號測試 |
| 📦 APK 確認 | URL 來自 APK environment.json 或程式碼;method/body 未必都有封包佐證 |
若靜態分析與實際封包不同,以封包結果為準。
2. 基本資訊
Production base URL:
https://crm-tw.akindo-sushiro.co.jp/api/2.0
Region:
TW
App 觀察到的 API User-Agent:
User-Agent: Dart/3.6 (dart:io)
POST body 主要使用:
Content-Type: application/x-www-form-urlencoded; charset=utf-8
不要測試 APK 中的 DEV/UAT 主機;本文件只涵蓋 production 與自有帳號。
3. 驗證模型
API 依路徑分成三層:
| 路徑 | 驗證方式 | 用途 |
|---|---|---|
/info/* |
通常不需驗證 | 店舖、時段、座位設定、系統設定 |
/remote/* |
guid 或 customer context |
登入、即時候位、公開票券狀態 |
/remote_auth/* |
HTTP Basic | 會員資料、預約訂位、會員票券 |
remote_auth 的 Basic credential 是:
base64("<email>:<password>")
範例:
Authorization: Basic <REDACTED>
X-SushiroApp 可由官方 App 帶入,但目前 live 測試並未證明它是必要欄位。
4. 核心流程
flowchart LR
A[取得或保存裝置 GUID] --> B[POST remote/login]
B --> C[取得 customerid]
C --> D[查詢店舖、seat config 與 reservation timeslots]
D --> E[取得目前 FCM registration token]
E --> F[POST remote_auth/newreservation]
F --> G[GET remote_auth/opentickets]
G --> H[POST remote_auth/cancel]
H --> I[再次查詢 opentickets 確認清空]
5. 已確認 API
通用功能
| 章節/用途 | Method/Endpoint | 證據 |
|---|---|---|
| 5.1 登入 | POST /remote/login |
✅/🔬 |
| 5.2 查詢店舖 | GET /info/storelist |
📦/部分 🔬 |
| 5.6 查詢會員票券/預約 | GET /remote_auth/opentickets |
✅/🔬 |
| 5.6 查詢裝置票券 | GET /remote/opentickets |
📦 |
| 5.10 查詢各類候位佇列 | GET /remote/groupqueues |
✅/🔬 |
| 5.10 查詢票號前方組數 | GET /remote/storequeuecount |
✅/🔬 |
| 5.11 綁定/關注店內票券 | PUT /remote/watchticket |
✅/🔬(E072) |
預約訂位
| 章節/用途 | Method/Endpoint | 證據 |
|---|---|---|
| 5.3 查詢座位設定 | GET /info/seatconfig |
✅/🔬 |
| 5.4 查詢預約時段 | GET /info/reservationtimeslots |
✅/🔬 |
| 5.5 建立預約 | POST /remote_auth/newreservation |
✅/🔬 |
| 5.7 取消預約 | POST /remote_auth/cancel |
✅/🔬 |
即時候位
| 章節/用途 | Method/Endpoint | 證據 |
|---|---|---|
| 5.8 建立即時候位票券 | POST /remote/newticket |
✅/🔬 |
| 5.9 取消即時候位票券 | POST /remote/cancel |
✅/🔬 |
5.1 登入
✅ 封包/🔬 Live 測試
POST /api/2.0/remote/login
Content-Type: application/x-www-form-urlencoded
Body:
email=<EMAIL>&password=<PASSWORD>®ion=TW
成功:
{
"status": "SUCCESS",
"confirmed": "YES",
"customerid": "<CUSTOMER_ID>",
"kana": "",
"privacyAgree": "true",
"termsAgree": "true"
}
錯誤帳密通常回 HTTP 401。登入回應不應完整記錄,因為包含 customer ID 與帳號狀態。
5.2 查詢店舖
📦 APK 確認/部分 🔬 Live 測試
GET /api/2.0/info/storelist?guid=<GUID>®ion=TW
附近店舖:
GET /api/2.0/info/storelist?latitude=<LAT>&longitude=<LNG>&numresults=<N>®ion=TW
5.3 查詢座位設定
✅ 官方 App 封包確認/🔬 Live 成功測試
GET /api/2.0/info/seatconfig?storeid=<STORE_ID>&date=<YYYYMMDD>®ion=TW HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
實際捕獲的成功 request 沒有 Authorization。成功回應:
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
{
"seatConfig": 1
}
seatConfig 是 integer;目前只有確認回傳值與型別,尚未確認各數值對應哪些 tabletype 或 UI 配置,不應自行建立未經封包/App 行為驗證的映射。
5.4 查詢預約時段
✅ 官方 App 封包確認/🔬 Live 成功測試
GET /api/2.0/info/reservationtimeslots?storeid=<STORE_ID>&numpersons=<N>&guid=<GUID>&tabletype=<TYPE>®ion=TW HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
Authorization: Basic <REDACTED>
官方 App 的成功 request 有帶 Basic Authorization,但目前封包只能證明「有帶」,尚不能證明此 endpoint 在完全省略 Authorization 時一定失敗。
回應範例:
[
{
"storeId": "20",
"date": "20260902",
"start": "200000",
"end": "201500",
"availability": "AVAILABLE"
}
]
在呼叫 newreservation 前,應先確認目標時段仍為 AVAILABLE。
5.5 建立預約訂位
✅ 官方 App 封包確認/🔬 Live 成功測試
POST /api/2.0/remote_auth/newreservation
Authorization: Basic <REDACTED>
User-Agent: Dart/3.6 (dart:io)
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Body:
guid=<GUID>&storeid=<STORE_ID>&adult=<ADULT>&child=<CHILD>&tabletype=<TYPE>&date=<YYYYMMDD>&time=<HHMMSS>®ion=TW&googleregistrationid=<FCM_TOKEN>
| 欄位 | 型別 | 說明 |
|---|---|---|
guid |
string | App 每次安裝產生並保存的裝置 UUID |
storeid |
string | 店舖 ID |
adult |
integer | 成人數 |
child |
integer | 兒童數 |
tabletype |
string | 座位類型;已知 T、C、P |
date |
string | yyyyMMdd |
time |
string | HHmmss |
region |
string | 台灣為 TW |
googleregistrationid |
string | App 的 FCM registration token |
成功回應:
{
"ticketId": 0,
"storeId": "20",
"queueDate": "20260902",
"queueTime": "200000",
"number": "<REDACTED>",
"numAdult": 2,
"numChild": 0,
"tableType": "T",
"wait": 5,
"status": "WAITING",
"checkedIn": false,
"agsQRCodeURL": "",
"waitingGroup": 0,
"waitGroup": 0
}
wait、waitingGroup 與 waitGroup 的精確語意仍未完全確認,應保留後端原值。
googleregistrationid 注意事項
- 官方 App request 有帶此欄位。
- 完全省略可能造成
E010/HTTP 500。 - 後端可能先建立資料,再於推播階段失敗;收到 500 後不可直接盲目重試。
- 應先查詢
opentickets,確認是否已產生預約,再決定重試或取消。 - 隨意產生的 Firebase token 可能不屬於壽司郎的 Sender ID,無法可靠接收推播。
5.6 查詢目前票券/預約
✅ 官方 App 封包確認/🔬 Live 成功測試
會員版本:
GET /api/2.0/remote_auth/opentickets?region=TW HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
Authorization: Basic <REDACTED>
成功回應:
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
已捕獲的回應頂層包含三個 array。這筆封包同時觀察到 TICKETS 與 RESERVATIONS 各有資料,而 LOCALTICKETS 為空:
{
"TICKETS": [
{
"TICKET_DETAIL": {},
"STORE_INFO": {}
}
],
"LOCALTICKETS": [],
"RESERVATIONS": [
{
"TICKET_DETAIL": {},
"STORE_INFO": {}
}
]
}
頂層語意:
| 欄位 | 型別 | 本次觀察 |
|---|---|---|
TICKETS |
array | 即時候位票券;捕獲到 MISSED 狀態 |
LOCALTICKETS |
array | 本次為空,因此元素 schema 尚未確認 |
RESERVATIONS |
array | 預約訂位;捕獲到 WAITING 狀態 |
每個已觀察到的 array item 都包含 TICKET_DETAIL 與 STORE_INFO。
TICKET_DETAIL schema
| 欄位 | 型別 | 說明/觀察 |
|---|---|---|
ticketId |
integer | 後端票券 ID;敏感值 |
storeId |
string | 店舖 ID;注意此處不是 integer |
queueDate |
string | yyyyMMdd |
queueTime |
string | HHmmss |
number |
string | 顯示票號;可能有前導零 |
numAdult、numChild |
integer | 成人與兒童人數 |
tableType |
string | 座位類型 |
wait |
integer | 等待值;精確語意仍待確認 |
status |
string | 已觀察到 WAITING、MISSED |
checkedIn |
boolean | 是否已 check-in |
agsQRCodeURL |
string | QR code URL;本次為空字串 |
waitingGroup、waitGroup |
integer | 已觀察到 0、1 與 -1;精確語意仍待確認 |
start、end |
string | RESERVATIONS 觀察到的預約起訖時間,格式 HHmmss;即時票券未出現 |
STORE_INFO schema
同一份店舖資料會附在每張票券內。這筆封包觀察到的欄位型別:
| 型別 | 欄位 |
|---|---|
| integer | id、sortOrder、wait、waitTimeCounter、waitTimeCap、cancellationMobileMinutes、cancellationReservationMinutes、tablesCapacity、countersCapacity、所有 minCustomers*/maxCustomers*、waitingGroup、seatConfig、waitShowType、waitingGroupTable、waitingGroupCounter、waitingGroupPair |
| number | latitude、longitude |
| string | storeStatus、name、nameKana、nameEn、address、area、timezone、distance、netTicketStatus、remoteTicketingManualStatus、reservationStatus、checkinStatus、commencementDate、localTicketingStatus、clientVersion、region |
| boolean | requireNetTicketLogin、forceLocalMode、counterReservationsAllowed、isAgs、pairReservationsAllowed、showCheckinCode、showCheckinCodeDialog |
| nullable | openDate;本次為 null |
值得注意:TICKET_DETAIL.storeId 是 string,但 STORE_INFO.id 是 integer;STORE_INFO.distance 在本次封包也是 string。資料模型應依實際型別解析,不要因欄位語意相近而強制共用型別。
裝置版本:
GET /api/2.0/remote/opentickets?guid=<GUID>®ion=TW
建立預約或即時候位前應先檢查三個陣列,避免覆蓋使用者既有票券。取消預約後確認 RESERVATIONS 已清空;取消即時候位後則確認對應項目已從 TICKETS/LOCALTICKETS 消失。
5.7 取消預約訂位
✅ 官方 App 封包確認/🔬 Live 成功測試
POST /api/2.0/remote_auth/cancel
Authorization: Basic <REDACTED>
User-Agent: Dart/3.6 (dart:io)
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Body:
guid=<GUID>®ion=TW
實際捕獲的 request 沒有:
ticketId
storeId
date
time
number
後端似乎依 Basic 帳號、guid 與 region 找出有效預約。不要自行添加未經證實的 ticketId。
成功回應:
{
"status": "SUCCESS"
}
5.8 即時候位取號
✅ 官方 App 封包確認/🔬 Live 成功測試
POST /api/2.0/remote/newticket HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
Content-Type: application/x-www-form-urlencoded; charset=utf-8
實際捕獲的成功 request 沒有 Authorization: Basic。Body:
guid=<GUID>&storeid=20&adult=2&child=0&tabletype=T®ion=TW&googleregistrationid=<URL_ENCODED_FCM_TOKEN>&activecustomerid=<CUSTOMER_ID>
已確認的 body 欄位:
| 欄位 | 範例/型別 | 說明 |
|---|---|---|
guid |
UUID string | App/裝置識別值 |
storeid |
20 |
店舖 ID |
adult |
2 |
成人數量 |
child |
0 |
兒童數量 |
tabletype |
T |
座位類型;實際可用值依店舖設定 |
region |
TW |
區域 |
googleregistrationid |
URL-encoded string | FCM registration token;例如 token 中的 : 會編碼成 %3A |
activecustomerid |
integer-like string | 目前登入會員的 customer ID |
早期測試未帶 googleregistrationid 時持續得到 E010。activecustomerid 在欄位驗證測試中不是語法上的強制欄位,但官方 App 的成功會員流程會帶入;重現正常流程時不應任意省略。
成功回應:
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
以下已將 ticket ID、票號與精確日期時間去敏;欄位與型別來自實際回應:
{
"ticketId": 0,
"storeId": "20",
"queueDate": "<YYYYMMDD>",
"queueTime": "<HHMMSS>",
"number": "<QUEUE_NUMBER>",
"numAdult": 2,
"numChild": 0,
"tableType": "T",
"wait": 5,
"status": "WAITING",
"checkedIn": false,
"agsQRCodeURL": "",
"waitingGroup": 0,
"waitGroup": 1
}
其中 ticketId: 0 是去敏佔位值,實際型別為 integer。其他觀察:
storeId、number、queueDate與queueTime是 string;number可能有前導零,不應轉成 integer。- 新建立的票券狀態為
WAITING,此例checkedIn為false。 wait、waitingGroup與waitGroup是建立當下的快照;三者的精確業務定義仍待確認。agsQRCodeURL在這筆建立票券回應中是空字串,不能據此假設所有狀態都為空。
5.9 取消即時候位取號
✅ 官方 App 封包確認/🔬 Live 成功測試
POST /api/2.0/remote/cancel HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Body:
guid=<GUID>®ion=TW
實際捕獲的成功 request 沒有:
Authorization
ticketId
storeId
number
activecustomerid
googleregistrationid
成功回應:
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
{
"status": "SUCCESS"
}
後端似乎依 guid、region 與目前有效的即時候位狀態找到要取消的票券。這是根據成功封包作出的推論;不要自行加入未經證實的 ticketId 或其他欄位。
此 endpoint 與預約訂位取消不同:
| 用途 | Endpoint | Basic Authorization |
|---|---|---|
| 取消即時候位 | POST /remote/cancel |
實際封包沒有 |
| 取消預約訂位 | POST /remote_auth/cancel |
實際封包有 |
5.10 店舖候位狀態
✅ 官方 App 封包確認/🔬 Live 成功測試
5.10.1 各類候位佇列
GET /api/2.0/remote/groupqueues?storeid=<STORE_ID>®ion=TW HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
實際捕獲的成功 request 沒有 Authorization。成功回應:
{
"boothQueue": [],
"counterQueue": [],
"mixedQueue": [],
"reservationQueue": [],
"reservationCounterQueue": [],
"reservationBoothQueue": [],
"storeQueue": [],
"storeCounterQueue": [],
"storeBoothQueue": [],
"separateQueue": 0
}
這筆回應中的九個 *Queue 欄位都是 array,separateQueue 是 integer。由於捕獲時所有 array 都是空的,目前尚未確認各 array 元素的 schema,也不能從單一空結果推斷它們永遠為空。
5.10.2 特定票號前方組數
GET /api/2.0/remote/storequeuecount?storeid=<STORE_ID>&ticketNo=<QUEUE_NUMBER>®ion=TW HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
實際捕獲的成功 request 同樣沒有 Authorization。成功回應是 JSON number primitive,不是 object:
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
0
此例回傳 integer 0。依 endpoint 用途推測它代表指定票號前方的等待組數,但是否包含正在叫號/過號票券仍待 App 行為或更多封包確認。ticketNo 應沿用 App/後端提供的值,不要自行轉型而遺失可能的前導零。
官方 App 的候位查詢輪詢間隔約為 60 秒;不要高頻輪詢。
5.11 綁定/關注店內票券
✅ 官方 App 封包確認/🔬 Live 錯誤路徑確認
依 endpoint 名稱與 request schema 推測,此 API 用於把既有店內號碼牌加入 App 的關注/追蹤狀態。Method 已由封包確認為 PUT,不是 endpoint 索引先前推測的 POST:
PUT /api/2.0/remote/watchticket HTTP/2
Host: crm-tw.akindo-sushiro.co.jp
User-Agent: Dart/3.6 (dart:io)
Content-Type: application/x-www-form-urlencoded; charset=utf-8
實際捕獲的 request 沒有 Authorization。Body:
guid=<GUID>®ion=TW&googleregistrationid=<URL_ENCODED_FCM_TOKEN>&storeid=<STORE_ID>&ticketnum=<TICKET_NUMBER>
| 欄位 | 型別 | 說明 |
|---|---|---|
guid |
UUID string | App/裝置識別值 |
region |
string | 台灣為 TW |
googleregistrationid |
URL-encoded string | FCM registration token |
storeid |
string | 號碼牌所屬店舖 ID |
ticketnum |
string | 店內號碼牌;應保留可能的前導零 |
以不存在/無效票號測試時,回應:
HTTP/2 400 Bad Request
Content-Type: application/json;charset=UTF-8
{
"message": "查無此號碼牌號碼,請再次確認您手中的號碼牌\n\nPlease check your ticket number.",
"code": "E072"
}
這筆封包只確認 request schema 與 E072 錯誤路徑,尚未確認有效票號的成功 response。不要為了探測號碼而批次嘗試 ticket number。
6. APK 內的完整端點索引
以下 URL 來自 APK 設定。沒有逐一列出 body 的端點,仍需以實際封包確認 method、欄位名稱與必要性。
/info 公開資訊
| 用途 | Endpoint | 證據 |
|---|---|---|
| 店舖列表/搜尋 | GET /info/storelist |
📦/部分 🔬 |
| 單店資料 | GET /info/store |
📦 |
| 多店資料 | GET /info/multiplestoredetails |
📦 |
| 預約時段 | GET /info/reservationtimeslots |
✅/🔬 |
| 座位設定 | GET /info/seatconfig |
✅/🔬 |
| App/CRM 設定 | GET /info/config |
📦 |
/remote 裝置或一般會員流程
| 用途 | Endpoint | 證據 |
|---|---|---|
| 登入 | POST /remote/login |
✅/🔬 |
| 建立帳號 | POST /remote/createaccount |
📦 |
| 忘記密碼 | POST /remote/requestreset |
📦 |
| 變更 Email | POST /remote/applychangeemailaddress |
📦 |
| 即時候位取號 | POST /remote/newticket |
✅/🔬 |
| 綁定/關注店內票券 | PUT /remote/watchticket |
✅/🔬(E072) |
| 取消即時候位 | POST /remote/cancel |
✅/🔬 |
| 裝置票券狀態 | GET /remote/opentickets |
📦 |
| 各類候位組數 | GET /remote/groupqueues |
✅/🔬 |
| 特定票號等待數 | GET /remote/storequeuecount |
✅/🔬 |
| 店舖叫號狀態 | GET /remote/storequeue |
📦 |
| 我的最愛列表 | GET /remote/favoritestore |
📦 |
| 新增最愛 | POST /remote/createfavoritestore |
📦 |
| 刪除最愛 | POST /remote/deletefavoritestore |
📦 |
| 優惠券 | GET /remote/couponlist |
📦 |
| Maido/外部服務 SSO | GET /remote/autologinurl |
📦 |
| 隱私政策同意 | POST /remote/updatepolicyagree |
📦 |
| 使用條款同意 | POST /remote/updatetermsagree |
📦 |
/remote_auth HTTP Basic 會員流程
| 用途 | Endpoint | 證據 |
|---|---|---|
| 會員資料 | GET /remote_auth/accountdetails |
🔬 |
| 更新會員資料 | POST /remote_auth/updateaccount |
🔬 |
| 變更密碼 | POST /remote_auth/changepwd |
🔬 |
| 重寄確認信 | POST /remote_auth/requestconfirmation |
📦 |
| 刪除帳號 | POST /remote_auth/removeaccount |
📦 |
| 建立預約訂位 | POST /remote_auth/newreservation |
✅/🔬 |
| 取消預約訂位 | POST /remote_auth/cancel |
✅/🔬 |
| 會員票券狀態 | GET /remote_auth/opentickets |
✅/🔬 |
| 發送 SMS 驗證碼 | POST /remote_auth/sendsmsauthenticationcode |
📦 |
| 驗證 SMS 驗證碼 | POST /remote_auth/checksmsauthenticationcode |
📦 |
| SMS 驗證狀態 | GET /remote_auth/smsverificationstatus |
📦 |
7. 已知錯誤
| HTTP/Code | 意義 | 建議處理 |
|---|---|---|
401 |
登入或 Basic credential 無效 | 重新登入;不要重複高速嘗試 |
E010/500 |
後端一般錯誤;曾由缺少或無法使用的推播 token 觸發 | 先查 opentickets,避免重複建立 |
E011 |
欄位值無效,例如未知 tabletype |
修正請求,不要原樣重試 |
E012 |
缺少必要欄位 | 依 property 修正 body |
E052 |
已存在預約/重複限制 | 查詢現有預約,不要再次建立 |
E072/400 |
watchticket 查無指定店內號碼牌 |
核對店舖與票號;不要批次枚舉 |
E096/503 |
台灣時間約 02:00–06:00 系統維護 | 維護時間後再試;不是 proxy/TLS 故障 |
E096 範例:
{
"message": "目前為系統維修時間,請稍候再嘗試使用(2:00~6:00)\nYou cannot use the system at this time(2:00~6:00)",
"code": "E096"
}
8. 安全與操作規則
下列資料視為敏感資訊:
email / password
Authorization: Basic ...
guid
customerid
googleregistrationid / FCM token
ticketId / number
- 不得提交到 Git。
- 不得輸出至一般 application log、Burp 匯出範例或錯誤頁面。
- 使用環境變數、secret store 或受保護的本機設定。
- 建立預約前先查
opentickets,避免覆蓋或取消原有訂位。 - 測試預約建立後應立即取消,並再次確認
RESERVATIONS為空。 - 輪詢至少維持官方 App 約 60 秒的間隔。
- 不進行大量取號、搶位或非自有帳號操作。
9. 尚待捕獲
- check-in request 與 QR code 流程
WAITING、MISSED以外的完整 ticket/reservation status enumwait、waitingGroup、waitGroup精確語意groupqueues非空 array 的元素 schema 與各 queue 欄位的精確分類規則storequeuecount是否包含正在叫號、過號或其他特殊狀態watchticket使用有效店內票號時的成功 response schemaT、C、P在台灣各店的實際對應座位名稱- 預約歷史
- SMS 驗證各種錯誤回應
- FCM token 失效、Sender ID 不符時 CRM 的完整錯誤映射