SMS DLR 狀態碼完全解讀 — SMD、DELIVRD、UNDELIV、REJECTD 到底代表什麼
做過 SMS 整合的人都遇過這個場景:API 回了 rc: 100,報表上顯示「成功」,但客戶說沒收到。
問題出在一個很多人沒分清楚的地方 — 「我們收下了」「我們送出去了」「客戶手機收到了」是三件不同的事,而很多系統把它們當成同一件。
這篇把 SMS 的回報機制拆開來講。
三個時間點,不是一個
一則短訊從你的伺服器到客戶手機,中間至少有三個可觀察的狀態:
- 同步回應 — 你 POST 之後立刻拿到的回覆。代表平台收下請求並排入佇列。
- 第一段 callback(送出請求回報) — 請求是否通過驗證並提交給電信商。這一段決定是否計費。
- 第二段 callback(送達回報 / DLR) — 電信商回報收件人裝置的最終狀態。
三個時間點可能相隔數秒到數小時。如果你的報表只記錄第 1 個,那張報表基本上只在告訴你「我送出了幾個 HTTP 請求」。
同步回應:只代表收下了
{ "rc": 100, "rm": "OK", "ref_id": "145737475" }
rc: 100 代表請求格式正確、已接受。ref_id 是這次請求的識別碼 — 後面兩段 callback 都會帶同一個 ref_id,這是串起整條鏈的鑰匙。
如果請求本身有問題(例如缺了必填欄位),這裡就會直接回 rc: -100,並在 rm 說明原因。
第一段 callback:提交結果
這一段告訴你請求有沒有真的交到電信商手上。
成功(rc = 100,會計費):
| 狀態 | 意思 |
|---|---|
SMD | 有效發送,訊息已提交電信商等待派送 |
失敗(rc = -100,不計費):
| 狀態 | 意思 | 通常是什麼問題 |
|---|---|---|
IM | 地區號碼或手機號碼不正確 | 號碼格式沒清理乾淨,帶了 +、空格或前綴 |
OFCA | 號碼在拒收訊息登記冊中,已被過濾(香港) | 對方登記了拒收,且你沒有其同意 |
IUL | 號碼在拒收名單中,未發送 | 該號碼之前已退訂 |
IP | 點數不足 | 帳戶餘額用完 |
IS | 發送人名稱無效或未登記 | Sender ID 沒在你的帳戶上完成綁定 |
這五個失敗碼有一個共同點:它們都是你這邊可以事前避免的。 號碼清洗、退訂名單管理、餘額監控、Sender ID 綁定,全部在你的控制範圍內。
值得特別注意 IP:如果你的系統沒有監控餘額,大批量發送到一半沒點數,剩下的全部靜靜失敗。建議在發送前呼叫 /v1/acct/get-points-balance 檢查一次。
第二段 callback:真正的送達回報
電信商回報最終結果時才會有這一段。
| 狀態 | 意思 | 建議處理 |
|---|---|---|
DELIVRD | 已送達收件人裝置 | 這才是「送到了」 |
EXPIRED | 超過有效期仍未送達 | 重試前先確認號碼是否仍在使用 |
UNDELIV | 目的地不可達、網絡問題,或裝置關機 | 可以稍後重試,成功率不低 |
REJECTD | 內容無效、列入黑名單,或政策限制 | 不要原樣重送 — 改內容或查發送人設定 |
UNKNWN | 電信商無法判定最終狀態 | 不宜當失敗直接重發,先觀察比例 |
三個最常被誤處理的狀態
UNDELIV 被當成永久失敗。 這多半是暫時性的 — 關機、隧道裡、出國漫遊。直接把號碼標成無效會白白損失名單。合理做法是隔一段時間重試一到兩次。
REJECTD 被自動重送。 這是最浪費點數的做法。內容或政策問題不會因為再送一次就消失,只會再被拒一次。收到這個狀態應該告警給人看,不是丟進重試佇列。
UNKNWN 被算進失敗率。 這會讓你的送達率數字看起來比實際差。比較合理的做法是單獨統計它的比例 — 如果某條路由的 UNKNWN 比例明顯偏高,那本身就是一個訊號。
怎麼用這些資料對帳
用 ref_id 串起三個數字:
- 你送出的請求數
- 第一段回
SMD的筆數 ← 這是計費基準 - 第二段回
DELIVRD的筆數
三個數字之間的落差,就是你真正該關心的東西。另外可以用 /v1/sms/get-usage-report 以日期區間取回 sms_count 與 points_used,和自己的紀錄互相驗證。
如果一家供應商只給你「送出成功」一個數字,你其實無從判斷中間發生了什麼 — 這也是為什麼我們把每個失敗碼原樣轉給客戶,而不是包裝成一個好看的百分比。詳見誠實送達。
實作上的兩個提醒
Callback 要寫成冪等的。 用 ref_id + status 作為唯一鍵。同一則 callback 有可能重複送達,如果你的處理邏輯會扣庫存或發郵件,重複執行就出事了。
Callback URL 要先在後台設定。 沒設定的話這兩段回報根本不會發出來 — 很多人以為是自己的 endpoint 有問題,其實是根本沒開。
完整的欄位定義與範例 payload 在 SMS API 整合指南。