給工程師的 SMS API。

REST + JSON · x-api-key 認證 · 兩段式 DLR callback

一個 POST 送出訊息,兩次 callback 告訴你「提交了」與「送到了」 — 這兩件事我們分開回報,因為它們本來就不是同一回事。 香港 OFCA Sender ID、拒收登記冊過濾、跨區欄位差異,這頁全部寫清楚。

找的是產品面說明?請看 SMS 服務頁 — 100+ 香港證券行採用、雙路由 failover、# Sender ID 代辦。

POST /v1/sms/send
x-api-key: YOUR_API_KEY

{
  "sender": "XLEAD",
  "country_code": "852",
  "mobile": "91234567",
  "content": "閣下的登入驗證碼為 482910"
}

→ { "rc": 100, "ref_id": "145737475" }
→ callback 1: "status": "SMD"
→ callback 2: "status": "DELIVRD"
快速開始

一個 POST,第一條 SMS 就送出去

不用 SDK、不用 OAuth 流程。取得 API key 後貼上下面這段,改掉號碼就能跑。

  1. 1

    取得 API Key

    登入 X Lead 後台建立 API key。之後每個請求都在 header 帶上 x-api-key。

  2. 2

    呼叫 send endpoint

    POST 一個 JSON,一個請求對應一位收件人。回應立即帶回 ref_id。

  3. 3

    接收 callback

    在後台設定 Callback URL,我們把提交結果與電信商送達回報推回你的系統。

送出一條香港 SMS

curl -X POST https://api.connect.xleadfunnel.com/v1/sms/send \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "sender": "XLEAD",
    "country_code": "852",
    "mobile": "91234567",
    "content": "閣下的登入驗證碼為 482910",
    "send_also_ofca_registrants": false
  }'

同步回應

{
  "rc": 100,
  "rm": "OK",
  "ref_id": "145737475"
}

ref_id 是這次請求的識別碼 — 之後兩次 callback 都會帶同一個 ref_id,用它對帳。

API 參考

認證、端點與參數

一個 header 完成認證;三個端點覆蓋整條發送鏈:送出、查用量、查餘額。

認證

Base URL
https://api.connect.xleadfunnel.com
認證方式
Header:x-api-key: YOUR_API_KEY
編碼
UTF-8
請求格式
application/json(POST)

端點

  • POST /v1/sms/send 送出一條 SMS,一個請求對應一位收件人。
  • GET /v1/sms/get-usage-report 以日期區間查詢用量彙總(date_from / date_to,YYYY-MM-DD)。
  • GET /v1/acct/get-points-balance 查詢剩餘點數餘額。SMS 與 MMS 共用同一池點數。

POST /v1/sms/send 參數

參數型別必填說明
country_code string 必填 地區號碼,1–3 位數字,不含前置 0。香港為 852。
mobile string 必填 收件人號碼,最多 10 位數字,可帶前置 0。
content string 選填 訊息內容,UTF-8。
sender string 選填 發送人名稱,3–11 位英數字。發往香港必須提供已登記的 Sender ID;發往台灣則不需要此欄位。
send_also_ofca_registrants boolean 選填 預設 false — 不發送給已登入 OFCA 拒收訊息登記冊的號碼。僅適用於香港。

回應欄位

參數 型別 說明
rc integer 100 = OK;-100 = 輸入無效
rm string 回應訊息,失敗時帶原因說明
ref_id string 本次請求的識別碼,後續 callback 會帶同一個值

地區差異:一個欄位的分別

sender 這個欄位是否必填,由收件地區的法規決定 — 這也是為什麼跨區發送不能只寫一套 payload。

+852 · 香港

必須提供已登記的 Sender ID,否則請求會被拒。另可用 send_also_ofca_registrants 控制是否略過 OFCA 拒收登記冊。

+886 · 台灣

不需提供 sender 欄位。省略即可,帶了反而可能造成驗證失敗。

DLR / Callback

兩段式回報 — 提交結果,然後才是送達結果

「已提交」不等於「已送達」。我們把兩件事分成兩次 callback,讓你分得清楚是自己的資料有問題,還是電信商那端沒送到。

你需要先登入後台設定 Callback URL,才會收到這些回報。

Callback 內容格式

rc
回應碼
rm
回應訊息,如有則附詳細說明
ref_id
原本 API 呼叫時回傳的 reference id
status
本次請求的提交或送達狀態

Callback 範例

// 第一段 — 已提交電信商,本則扣點
{ "rc": 100, "rm": "OK", "ref_id": "145737475", "status": "SMD" }

// 第一段 — 驗證失敗,不扣點
{ "rc": -100, "rm": "...", "ref_id": "145737476", "status": "IM" }

// 第二段 — 電信商回報最終送達狀態
{ "rc": 100, "rm": "OK", "ref_id": "145737475", "status": "DELIVRD" }
1

第一段 · 送出請求回報

你的請求是否通過驗證並提交給電信商。這一段決定是否扣點。

rc = 100 會扣點
SMD
有效發送,訊息已提交電信商等待派送
rc = -100 不扣點
IM
地區號碼或手機號碼不正確
OFCA
號碼在 OFCA 拒收訊息登記冊中,已被過濾(僅香港)
IUL
號碼在拒收名單(unsend list)中,未發送
IP
點數不足
IS
無效的發送人名稱,或該 Sender ID 未在你的帳戶登記
2

第二段 · 送達回報(DLR)

電信商回傳收件人裝置的最終狀態時,我們原樣轉給你。

rc = 100
DELIVRD
電信商已成功送達收件人裝置
EXPIRED
處理超過有效期,未能送達
UNDELIV
無法送達 — 目的地不可達、網絡問題,或收件人裝置關機
REJECTD
被拒 — 內容無效、列入黑名單,或政策限制
UNKNWN
電信商無法判定訊息的最終送達狀態

為什麼我們把失敗碼全部列出來

有些供應商只回「submitted」就當成功,帳單照收。我們把提交與送達拆成兩段、把每個失敗碼原樣轉給你,是因為你需要能對帳:收多少筆、提交多少筆、真正送達多少筆,三個數字都要對得上。

了解誠實送達 →
整合範例

用你手上的語言接進去

同一個 POST,六種寫法。全部只依賴各語言的標準 HTTP 用戶端 — 不需要安裝我們的 SDK。

cURL

curl -X POST https://api.connect.xleadfunnel.com/v1/sms/send \
  -H "Content-Type: application/json" \
  -H "x-api-key: $XLEAD_API_KEY" \
  -d '{
    "sender": "XLEAD",
    "country_code": "852",
    "mobile": "91234567",
    "content": "閣下的登入驗證碼為 482910"
  }'

Node.js

const res = await fetch('https://api.connect.xleadfunnel.com/v1/sms/send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.XLEAD_API_KEY,
  },
  body: JSON.stringify({
    sender: 'XLEAD',
    country_code: '852',
    mobile: '91234567',
    content: '閣下的登入驗證碼為 482910',
  }),
});

const { rc, rm, ref_id } = await res.json();
if (rc !== 100) throw new Error(`send failed: ${rm}`);
console.log('queued as', ref_id);

Python

import os, requests

res = requests.post(
    'https://api.connect.xleadfunnel.com/v1/sms/send',
    headers={'x-api-key': os.environ['XLEAD_API_KEY']},
    json={
        'sender': 'XLEAD',
        'country_code': '852',
        'mobile': '91234567',
        'content': '閣下的登入驗證碼為 482910',
    },
    timeout=10,
)

data = res.json()
if data['rc'] != 100:
    raise RuntimeError(f"send failed: {data['rm']}")
print('queued as', data['ref_id'])

PHP

<?php
$ch = curl_init('https://api.connect.xleadfunnel.com/v1/sms/send');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'x-api-key: ' . getenv('XLEAD_API_KEY'),
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'sender'       => 'XLEAD',
        'country_code' => '852',
        'mobile'       => '91234567',
        'content'      => '閣下的登入驗證碼為 482910',
    ], JSON_UNESCAPED_UNICODE),
]);

$data = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($data['rc'] !== 100) {
    throw new RuntimeException('send failed: ' . $data['rm']);
}
echo 'queued as ' . $data['ref_id'];

Java

var body = """
    {"sender":"XLEAD","country_code":"852",
     "mobile":"91234567","content":"閣下的登入驗證碼為 482910"}
    """;

var req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.connect.xleadfunnel.com/v1/sms/send"))
    .header("Content-Type", "application/json")
    .header("x-api-key", System.getenv("XLEAD_API_KEY"))
    .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
    .build();

var res = HttpClient.newHttpClient()
    .send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));

System.out.println(res.body());   // {"rc":100,"rm":"OK","ref_id":"..."}

C# / .NET

using var http = new HttpClient();
http.DefaultRequestHeaders.Add(
    "x-api-key", Environment.GetEnvironmentVariable("XLEAD_API_KEY"));

var payload = new {
    sender       = "XLEAD",
    country_code = "852",
    mobile       = "91234567",
    content      = "閣下的登入驗證碼為 482910"
};

var res = await http.PostAsJsonAsync("https://api.connect.xleadfunnel.com/v1/sms/send", payload);
var data = await res.Content.ReadFromJsonAsync<JsonElement>();

if (data.GetProperty("rc").GetInt32() != 100)
    throw new Exception(data.GetProperty("rm").GetString());

正式環境請把 API key 放進環境變數,不要寫死在程式碼裡。

開始整合

從開戶到上線,3 個步驟

不需要先簽長約才能試。多數客戶第一週就把測試發送跑通,Sender ID 登記在背景同步進行。

  1. 1

    開立帳戶

    聯絡我們說明用途與預估量,我們開好帳戶並配置適合你收件地區的路由。

  2. 2

    建立 API key

    在後台產生 key,之後每個請求以 header 帶上 x-api-key。只放伺服器端。

  3. 3

    3 分鐘設定 API 並測試

    先在後台一鍵發一則測試短訊確認路由與顯示,再照上方 Quickstart 用 API 發出第一則訊息 — 前後約 3 分鐘。

跑通之後,再補上

  • +

    設定 Callback URL(可選)

    需要接收兩段式 DLR 回報時,在後台填入你的 endpoint — 沒設定就收不到任何 DLR。

  • +

    登記 # Sender ID(建立品牌)

    發往香港需要已登記的發送人名稱,也是收件匣裡的品牌識別。命名檢核、文件、安全網絡我們代辦,可與整合並行。

想先要測試 key?

需要以 API 做整合測試的話,聯絡我們索取測試用 API key 與少量測試點數,不必等正式合約。

索取測試 key →

整合常見問題

一個請求可以發給多位收件人嗎? +

不行。/v1/sms/send 是一個請求對應一位收件人。要群發就在你這邊迴圈呼叫,每一筆都會拿到自己的 ref_id,對帳與重送都以單筆為單位。若你要的是大批量排程廣播,後台的發送介面比自己寫迴圈更合適。

rc = 100 是不是代表客戶已經收到? +

不是。同步回應的 rc = 100 只代表我們收下並排入佇列。真正的提交結果在第一段 callback(status = SMD 才是已提交電信商),最終是否送到收件人裝置要看第二段 callback 的 DELIVRD。把這三件事混為一談,是自建報表最常見的錯誤。

為什麼發往香港一定要 sender,發往台灣卻不用? +

因為規範不同。香港 OFCA 實施短訊發送人登記制,商業訊息必須使用已登記的發送人名稱,所以 sender 必填且必須是你帳戶下已登記的 Sender ID;台灣不採這套制度,該欄位不需提供。跨區發送時請依收件地區組裝 payload。

send_also_ofca_registrants 應該設什麼? +

預設 false,代表不發送給已登入 OFCA 拒收訊息登記冊的號碼 — 推廣訊息應維持這個預設。只有在該訊息屬於登記冊規管範圍以外的情況(例如收件人與你之間已有既存關係下的交易類通知),才需要考慮改成 true,而責任在發送方。不確定就保持 false。

收不到 callback,要檢查什麼? +

依序檢查:後台是否已設定 Callback URL;該 URL 是否對外可達且回應 2xx;是否走 HTTPS 且憑證有效;有沒有被防火牆擋掉。另外請把 callback 處理寫成冪等的 — 以 ref_id + status 作為唯一鍵,重複收到同一則不應該重複扣庫存或重複發信。

DELIVRD 以外的狀態要怎麼處理? +

UNDELIV 多半是關機或不在服務範圍,可以稍後重試;EXPIRED 代表在有效期內一直送不到,重試前先確認號碼仍在使用;REJECTD 是內容或政策問題,重送同樣內容只會再被拒,要先改內容或查發送人設定;UNKNWN 是電信商無法判定,不宜當成失敗直接重發,建議先累積觀察比例。

怎麼對帳? +

ref_id 串起三個點:你送出的請求數、第一段 callback 回 SMD 的筆數(這是計費基準)、第二段 callback 回 DELIVRD 的筆數。另外可用 /v1/sms/get-usage-report 以日期區間取回 sms_countpoints_used,跟你自己的紀錄互相驗證。

有沒有測試環境? +

開戶後即可在後台一鍵測試發送,管理員不必先寫程式就能確認 Sender ID 與路由設定正確。需要以 API 進行整合測試,聯絡我們索取測試用 API key 與少量測試點數。

準備好開始了嗎?

聯絡我們 — 快速開戶後即可發送。測試期間提供免費額度,讓你先完成串接及操作,確認平台切合你的實際使用場景。