NT Notify API

概述

此 API 提供類似 NT Notify 的功能,允許應用程式向使用者發送通知。您需要生成 API Token 進行身份驗證。

重要提示: 所有請求都必須包含有效的 API Token。
新功能: 我們現在支持針對特定用戶發送、群發和廣播訊息。查看下方文檔了解更多資訊。

API 端點

查詢服務狀態

端點: GET /api/status

描述: 查詢 API 服務狀態。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
成功回應
{
    "status": 200,
    "message": "服務運行中",
    "data": {
        "app": "NT-Notify",
        "status": "ok",
        "server_time": "2026-09-08T12:34:56+08:00"
    }
}
失敗回應
{
    "status": 401,
    "message": "Token 無效或未提供"
}
回應參數說明
參數名稱 類型 描述
status integer HTTP 狀態碼
message string 狀態訊息
data.app string 服務名稱
data.status string 服務狀態,固定為 ok
data.server_time string 伺服器目前時間(ISO 8601 格式)
範例
curl -X GET \
  /api/status \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
<?php
$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL => '/api/status',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_API_TOKEN',
    ],
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
fetch('/api/status', {
    method: 'GET',
    headers: {
        'Authorization': 'Bearer YOUR_API_TOKEN'
    }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
發送通知

端點: POST /api/notify

描述: 發送通知訊息。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
請求參數
參數名稱 類型 必填 描述
message string 是 要發送的訊息內容 (最多 5000 字元)
title string 否 訊息標題 (最多 255 字元)
image_url string 否 要附加的圖片 URL
recipient_id string 否 指定單一接收者的 LINE 用戶 ID
recipient_ids array 否 指定多個接收者的 LINE 用戶 ID 陣列,用於群發訊息 (最多 500 位)
send_to_all boolean 否 是否發送給所有會員 (廣播),設為 true 時需謹慎使用
recipient_group_id string 否 指定單一接收群組的 LINE 群組 ID
recipient_group_ids array 或 string 否 指定多個接收群組的 LINE 群組 ID 陣列。也可以用逗號分隔的字串,例如: "id1,id2,id3"
注意: recipient_id、recipient_ids、recipient_group_id、recipient_group_ids 和 send_to_all 參數不能同時使用。您只能選擇其中一種發送方式。如果都不指定,則使用預設接收者。
成功回應
{
    "success": true,
    "message": "訊息已發送",
    "data": {
        "recipients_count": 1,
        "sent_at": "2023-10-15T12:34:56Z"
    }
}
失敗回應
{
    "success": false,
    "message": "驗證失敗",
    "errors": {
        "message": ["訊息內容不能為空"]
    }
}
回應參數說明
參數名稱 類型 描述
success boolean 是否成功 (true: 成功, false: 失敗)
message string 狀態訊息
data.recipients_count integer 接收訊息的用戶數量 (僅在成功時提供)
data.sent_at string 訊息發送時間 (ISO 8601 格式,僅在成功時提供)
errors object 驗證錯誤訊息 (僅在參數驗證失敗時提供)
使用範例
# 基本發送
curl -X POST \
  /api/notify \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
      "message": "Hello World",
      "title": "通知標題",
      "image_url": "https://example.com/image.jpg"
  }'

# 發送給特定用戶
curl -X POST \
  /api/notify \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
      "message": "This is a personal message",
      "title": "個人通知",
      "recipient_id": "U4af4980629..."
  }'

# 群發給多個用戶
curl -X POST \
  /api/notify \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
      "message": "This is a group message",
      "title": "群組通知",
      "recipient_ids": ["U4af4980629...", "U8c6d6tx42b..."]
  }'

# 廣播給所有用戶
curl -X POST \
  /api/notify \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
      "message": "This is a broadcast message",
      "title": "廣播通知",
      "send_to_all": true
  }'
<?php
/**
 * 發送簡單通知訊息
 */
function sendNotification($message, $title = null, $imageUrl = null) {
    $apiUrl = 'https://notify.example.com/api/notify';
    $apiToken = 'YOUR_API_TOKEN_HERE';
    
    // 準備請求資料
    $data = [
        'message' => $message
    ];
    
    // 如果有標題,加入 data
    if (!empty($title)) {
        $data['title'] = $title;
    }
    
    // 如果有圖片 URL,加入 data
    if (!empty($imageUrl)) {
        $data['image_url'] = $imageUrl;
    }
    
    // 初始化 cURL
    $ch = curl_init($apiUrl);
    
    // 設定 cURL 選項
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $apiToken
    ]);
    
    // 執行 cURL 請求
    $response = curl_exec($ch);
    curl_close($ch);
    
    return json_decode($response, true);
}

/**
 * 發送訊息給特定使用者
 */
function sendToUser($message, $recipientId, $title = null, $imageUrl = null) {
    $apiUrl = 'https://notify.example.com/api/notify';
    $apiToken = 'YOUR_API_TOKEN_HERE';
    
    // 準備請求資料
    $data = [
        'message' => $message,
        'recipient_id' => $recipientId
    ];
    
    // 如果有標題,加入 data
    if (!empty($title)) {
        $data['title'] = $title;
    }
    
    // 如果有圖片 URL,加入 data
    if (!empty($imageUrl)) {
        $data['image_url'] = $imageUrl;
    }
    
    // 初始化 cURL
    $ch = curl_init($apiUrl);
    
    // 設定 cURL 選項
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $apiToken
    ]);
    
    // 執行 cURL 請求
    $response = curl_exec($ch);
    curl_close($ch);
    
    return json_decode($response, true);
}

// 使用範例
// sendNotification('這是一條通知訊息', '測試標題');
// sendToUser('這是發送給特定用戶的訊息', 'U4af4980629...', '個人通知');
import requests
import json

# 設定 NT-Notify API 的 URL 和 API Token
API_URL = 'https://notify.example.com/api/notify'
API_TOKEN = 'YOUR_API_TOKEN_HERE'  # 請將此替換為您的實際 API Token


def send_notification(message, title=None, image_url=None):
    """
    發送簡單通知訊息
    """
    # 準備請求資料
    data = {'message': message}
    
    # 如果有標題,加入 data
    if title:
        data['title'] = title
    
    # 如果有圖片 URL,加入 data
    if image_url:
        data['image_url'] = image_url
    
    # 設定請求標頭
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {API_TOKEN}'
    }
    
    # 發送 API 請求
    response = requests.post(API_URL, headers=headers, json=data)
    
    return response.json()


def send_to_user(message, recipient_id, title=None, image_url=None):
    """
    發送訊息給特定使用者
    """
    # 準備請求資料
    data = {
        'message': message,
        'recipient_id': recipient_id
    }
    
    # 如果有標題,加入 data
    if title:
        data['title'] = title
    
    # 如果有圖片 URL,加入 data
    if image_url:
        data['image_url'] = image_url
    
    # 設定請求標頭
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {API_TOKEN}'
    }
    
    # 發送 API 請求
    response = requests.post(API_URL, headers=headers, json=data)
    
    return response.json()

# 使用範例
# result = send_notification("這是一條測試訊息", "測試標題")
# print(result)
// 設定 API 相關常數
const API_URL = '/api/notify';
const API_TOKEN = 'YOUR_API_TOKEN';

/**
 * 發送基本通知
 */
async function sendNotification(message, title, imageUrl) {
    // 準備請求資料
    const data = { message };
    
    // 如果有標題,加入 data
    if (title) {
        data.title = title;
    }
    
    // 如果有圖片 URL,加入 data
    if (imageUrl) {
        data.image_url = imageUrl;
    }
    
    try {
        const response = await fetch(API_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${API_TOKEN}`
            },
            body: JSON.stringify(data)
        });
        
        return await response.json();
    } catch (error) {
        console.error('Error:', error);
        return { success: false, message: error.message };
    }
}

/**
 * 發送訊息給特定用戶
 */
async function sendToUser(message, recipientId, title, imageUrl) {
    // 準備請求資料
    const data = { 
        message,
        recipient_id: recipientId
    };
    
    // 如果有標題,加入 data
    if (title) {
        data.title = title;
    }
    
    // 如果有圖片 URL,加入 data
    if (imageUrl) {
        data.image_url = imageUrl;
    }
    
    try {
        const response = await fetch(API_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${API_TOKEN}`
            },
            body: JSON.stringify(data)
        });
        
        return await response.json();
    } catch (error) {
        console.error('Error:', error);
        return { success: false, message: error.message };
    }
}

// 使用範例
// sendNotification('Hello World', '通知標題', 'https://example.com/image.jpg')
//   .then(data => console.log(data));
// 設定 NT-Notify API 的 URL 和 API Token
var API_URL = 'https://notify.example.com/api/notify';
var API_TOKEN = 'YOUR_API_TOKEN_HERE'; // 請將此替換為您的實際 API Token

/**
 * 發送單一訊息通知
 */
function sendNotification(message, title, imageUrl) {
  // 準備請求資料
  var payload = {
    'message': message
  };
  
  // 如果有標題,加入 payload
  if (title && title.trim() !== '') {
    payload.title = title;
  }
  
  // 如果有圖片 URL,加入 payload
  if (imageUrl && imageUrl.trim() !== '') {
    payload.image_url = imageUrl;
  }
  
  // 設定 API 請求選項
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + API_TOKEN
    },
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };
  
  // 發送 API 請求
  var response = UrlFetchApp.fetch(API_URL, options);
  
  // 解析 API 回應
  var result = JSON.parse(response.getContentText());
  
  // 記錄結果
  Logger.log('發送結果: ' + result.message);
  
  return result;
}

/**
 * 發送訊息給特定使用者
 */
function sendToUser(message, recipientId, title, imageUrl) {
  // 準備請求資料
  var payload = {
    'message': message,
    'recipient_id': recipientId
  };
  
  // 如果有標題,加入 payload
  if (title && title.trim() !== '') {
    payload.title = title;
  }
  
  // 如果有圖片 URL,加入 payload
  if (imageUrl && imageUrl.trim() !== '') {
    payload.image_url = imageUrl;
  }
  
  // 設定 API 請求選項
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + API_TOKEN
    },
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };
  
  // 發送 API 請求
  var response = UrlFetchApp.fetch(API_URL, options);
  
  return JSON.parse(response.getContentText());
}

/**
 * 發送訊息給特定群組
 */
function sendToSpecificGroup() {
  sendWebhook('這是發送給特定群組的訊息', {
    'recipient_group_id': 'C4af4980629...'
  });
}

/**
 * 群發訊息給多個群組
 */
function sendToMultipleGroups() {
  sendWebhook('這是群發給多個群組的訊息', {
    'recipient_group_ids': ['C4af4980629...', 'C8c6d6tx42b...']
  });
}

// 使用範例
// function testNotify() {
//   sendNotification('這是一條通知訊息', '測試標題');
// }
樣本程式碼

您可以下載以下完整的範例程式碼:


取得訊息紀錄

本系統提供 API 查詢 LINE Bot 收到的訊息紀錄,支援多種篩選條件。認證方式與其他端點相同,使用 API Token(Authorization: Bearer YOUR_API_TOKEN),無需另外登入取得 Token。

取得所有 Bot 的接收訊息(跨 Bot 查詢)

端點: GET /api/messages

描述: 查詢目前登入使用者旗下「所有 Bot」接收到的訊息,可加上 bot_id 參數縮小範圍,適合跨 Bot 統一查詢。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
查詢參數
參數名稱 類型 必填 描述
bot_id integer 否 篩選特定 Bot(不填則查詢所有 Bot)
line_user_id string 否 篩選特定 LINE 用戶
line_group_id string 否 篩選特定 LINE 群組
source_type string 否 user 或 group
message_type string 否 訊息類型:text、image、sticker 等
start_date string 否 起始日期,格式 Y-m-d
end_date string 否 結束日期,格式 Y-m-d
per_page integer 否 每頁筆數(預設 15,最大 100)
direction string 否 in(接收)、out(發送)或 all;不填等同 all。其他值回 422
exclude_inbound_copies boolean 否 帶 1 時排除收訊複製(is_inbound_copy=true 的列),避免同一則收到的訊息出現兩次。不帶時行為不變
回應附加資訊(meta)
欄位說明
meta.server_time伺服器產生回應的時間(ISO-8601,含時區)
meta.last_received_at在同樣篩選條件下(不受分頁影響),最後一筆接收訊息的時間;沒有符合的訊息時為 null。只反映接收方向,direction=out 時固定為 null,不代表沒有收到任何訊息
同一則收到的訊息可能出現兩次:系統收到 LINE 訊息時,除了存成接收紀錄(direction=in),也會複製一份到發送紀錄供後台紀錄頁檢視;這份複製在 API 中會以 direction=out、channel=line 出現,但它其實是對方說的話,不是 Bot 發出的訊息。 因此不帶 direction(或 direction=all)時,每則收到的訊息會出現兩筆。 要查「客戶說了什麼」,請帶 direction=in;要同時看 Bot 發出的訊息又不想重複,請帶 exclude_inbound_copies=1。
每筆訊息的附加欄位(附加在既有欄位之後)
欄位說明
unsent_at對方在 LINE 撤回該則訊息的時間;未撤回為 null。撤回的訊息仍會回傳(內容保留),請依此欄位自行決定是否顯示。
收訊複製(is_inbound_copy=true)的限制:只有「撤回標記」功能上線後寫入的複製能被標記;更早的複製一律為 null,不代表未撤回,請以 direction=in 那筆為準
is_inbound_copytrue 表示這筆是收到訊息時寫入的複製(direction=out 且 channel=line),內容與對應的 direction=in 那筆重複
original_name收到的媒體/檔案在對方裝置上的原始檔名(LINE 只在 file 訊息提供 fileName);沒有或 direction=out 時為 null。content 的網址副檔名不一定正確,要顯示檔名請用這個欄位
file_size收到的媒體/檔案大小(bytes,整數);沒有或 direction=out 時為 null
media_mime系統實際偵測到的 MIME type(例如 application/zip);沒有或 direction=out 時為 null
使用範例
# 查詢所有 Bot 的接收訊息
curl -X GET "http://YOUR_DOMAIN/api/messages?per_page=20" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# 篩選某日期範圍內的群組訊息
curl -X GET "http://YOUR_DOMAIN/api/messages?source_type=group&start_date=2024-01-01&end_date=2024-01-31" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# 特定 Bot + 特定用戶
curl -X GET "http://YOUR_DOMAIN/api/messages?bot_id=1&line_user_id=U4af4980629..." \
  -H "Authorization: Bearer YOUR_API_TOKEN"
<?php
$apiUrl = 'https://YOUR_DOMAIN/api/messages';
$apiToken = 'YOUR_API_TOKEN';

$params = http_build_query([
    'source_type' => 'user',
    'start_date'  => '2024-01-01',
    'end_date'    => '2024-01-31',
    'per_page'    => 20,
]);

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $apiUrl . '?' . $params,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer {$apiToken}"],
]);

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

echo "共 " . $response['data']['total'] . " 則訊息\n";
foreach ($response['data']['data'] as $msg) {
    echo "[{$msg['created_at']}] {$msg['content']}\n";
}
const apiToken = 'YOUR_API_TOKEN';

async function getAllMessages(filters = {}) {
    const params = new URLSearchParams(filters).toString();
    const res = await fetch(`/api/messages?${params}`, {
        headers: { 'Authorization': `Bearer ${apiToken}` }
    });
    return await res.json();
}

// 查詢所有 Bot 今日的個人訊息
const today = new Date().toISOString().split('T')[0];
getAllMessages({ source_type: 'user', start_date: today })
    .then(data => {
        console.log(`共 ${data.data.total} 則訊息`);
        data.data.data.forEach(msg => {
            console.log(`[${msg.created_at}] ${msg.content}`);
        });
    });

群組分類

群組管理支援自訂分類(category / subcategory / tags / note),可用於「一群一個倉庫」的分群檢視。認證方式:API Token(Authorization: Bearer YOUR_API_TOKEN)。

取得群組清單(含分類資訊)

端點: GET /api/groups

描述: 取得目前 Token 擁有者名下所有群組,可用 bot_id、category 篩選。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
查詢參數
參數名稱類型必填描述
bot_idinteger否篩選特定 Bot
categorystring否篩選特定分類
成功回應
{
    "status": 200,
    "message": "取得群組清單成功",
    "data": [
        {
            "id": 1,
            "user_bot_id": 1,
            "line_group_id": "Cd48b3679f5e6564d8fdd2b180e518376",
            "group_name": "丟資料用",
            "category": "測試",
            "subcategory": null,
            "tags": null,
            "note": null,
            "created_at": "2026-09-07T22:30:30+08:00"
        }
    ]
}
錯誤回應
狀態碼說明
401Token 無效或未提供
更新單個群組的分類資訊

端點: PUT /api/groups/{id}/taxonomy

描述: {id} 為群組在本系統的內部 ID(GET /api/groups 回應中的 id,非 line_group_id)。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
請求參數
參數名稱類型必填描述
categorystring否分類(最多 255 字元)
subcategorystring否子分類(最多 255 字元)
tagsarray<string>否標籤(多值)
notestring否備註
成功回應
{
    "status": 200,
    "message": "群組分類更新成功",
    "data": { "id": 1, "category": "測試", "subcategory": null, "tags": null, "note": null }
}
錯誤回應
狀態碼說明
401Token 無效或未提供
403該群組不屬於目前 Token 的擁有者
404找不到指定的群組
422參數驗證失敗
範例
curl -X PUT "http://YOUR_DOMAIN/api/groups/1/taxonomy" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"category": "測試", "tags": ["重要", "供應商"]}'

群組訊息匯出

把某個群組的訊息一次匯出(文字格式 + 媒體 ZIP),採非同步處理:先建立匯出工作,再輪詢工作狀態取得下載連結。認證方式:API Token。

建立群組訊息匯出工作

端點: POST /api/groups/{line_group_id}/exports

描述: {line_group_id} 為 LINE 群組 ID(例如 Cd48b3679f5e6564d8fdd2b180e518376)。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
請求參數
參數名稱類型必填描述
formatstring否txt、csv(預設)或 jsonl
start_datestring否起始日期,格式 Y-m-d
end_datestring否結束日期,格式 Y-m-d
include_mediaboolean否是否一併打包照片/影片/檔案的 ZIP
message_typestring否篩選訊息類型
成功回應(202)
{
    "status": 202,
    "message": "匯出工作已建立",
    "job_id": 1,
    "export_status": "pending"
}
錯誤回應
狀態碼說明
401Token 無效或未提供
404找不到群組或沒有權限
422參數驗證失敗
範例
curl -X POST "http://YOUR_DOMAIN/api/groups/Cd48b3679f5e6564d8fdd2b180e518376/exports" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d "format=csv&include_media=1"
查詢匯出工作狀態

端點: GET /api/exports/{job_id}

描述: {job_id} 為建立匯出工作時回傳的 job_id。

成功回應
{
    "status": 200,
    "message": "取得匯出工作狀態成功",
    "data": {
        "job_id": 1,
        "status": "completed",
        "file_url": "https://.../exports/xxx.zip",
        "expires_at": "2026-09-09T22:30:30+08:00",
        "error": null
    }
}

status 可能為 pending、processing、completed 或 failed; file_url 只在 completed 且未過期時提供,下載連結有效期限至少 24 小時。

錯誤回應
狀態碼說明
401Token 無效或未提供
404找不到匯出工作或沒有權限

群組歷史聊天紀錄匯入

把 bot 加入群組前的歷史對話(LINE App 匯出的 .txt)與照片/影片補進同一個群組的倉庫,一併採非同步處理。認證方式:API Token。

建立歷史聊天紀錄匯入工作

端點: POST /api/groups/{line_group_id}/imports

描述: multipart/form-data 上傳。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
Content-Type: multipart/form-data
請求參數
參數名稱類型必填描述
chat_txtfile是LINE App 匯出的聊天紀錄 .txt(最大 5MB)
media[]file[]否照片/影片/檔案,可多個(每個最大 50MB)
name_mapstring(JSON)否「.txt 顯示名稱 → 會員」對應表,例如 {"TK Lin": "U4af..."}
成功回應(202)
{
    "status": 202,
    "message": "匯入工作已建立",
    "job_id": 1,
    "import_status": "pending"
}
錯誤回應
狀態碼說明
401Token 無效或未提供
404找不到群組或沒有權限
422參數錯誤(檔案格式/大小、name_map 非合法 JSON 等)
範例
curl -X POST "http://YOUR_DOMAIN/api/groups/Cd48b3679f5e6564d8fdd2b180e518376/imports" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "chat_txt=@/path/to/chat.txt" \
  -F "media[]=@/path/to/photo1.jpg" \
  -F "media[]=@/path/to/photo2.jpg" \
  -F 'name_map={"TK Lin":"U4af4980629..."}'
查詢匯入工作狀態

端點: GET /api/imports/{job_id}

成功回應
{
    "status": 200,
    "message": "取得匯入工作狀態成功",
    "data": {
        "id": 1,
        "group_id": "Cd48b3679f5e6564d8fdd2b180e518376",
        "status": "completed",
        "stats": { "inserted": 12, "skipped": 0, "media_matched": 2, "media_unmatched": 0 },
        "error_message": null
    }
}

status 可能為 pending、processing、completed 或 failed;stats 內容依實際解析結果而定。

錯誤回應
狀態碼說明
401Token 無效或未提供
404找不到匯入工作或沒有權限
撤銷整批匯入

端點: DELETE /api/imports/{job_id}

描述: 刪除該匯入批次寫入的所有訊息(origin = import),webhook 收到的訊息不受影響。

成功回應
{
    "status": 200,
    "message": "匯入批次已撤銷",
    "deleted_count": 12
}
錯誤回應
狀態碼說明
401Token 無效或未提供
404找不到匯入工作或沒有權限
範例
curl -X DELETE "http://YOUR_DOMAIN/api/imports/1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

主動發送訊息與附檔

以 LINE Bot 主動發送訊息給個人、會員或群組,可附圖片、影片或檔案。認證方式:API Token(Authorization: Bearer YOUR_API_TOKEN)。對外發送 API 目前僅支援 LINE。

發送訊息

端點: POST /api/messages/send

描述: 以指定 Bot 發送文字訊息,可附圖片/影片(公開 HTTPS 網址或 multipart 直接上傳)或檔案。只帶網址時可用 JSON;要上傳圖片、影片或檔案時請用 multipart/form-data。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
請求參數
參數名稱 類型 必填 描述
bot_id integer 是 發送用的 Bot ID,必須屬於此 Token 的擁有者
message string 是 文字內容,最多 5000 字(LINE 文字上限)。帶 file 時,系統會在末尾附上檔名、下載連結與期限,合計也不得超過 5000 字,否則回 422
target_type string 是 收件對象類型:direct、member、multicast、group、multigroup(見下表)
direct_user_id string target_type=direct 時 LINE userId
member_id integer target_type=member 時 會員 id(需屬於此 Bot 且為啟用狀態)
member_ids array of integer target_type=multicast 時 會員 id 陣列,最多 500 位
group_id string target_type=group 時 LINE 群組 ID(C 開頭)
group_ids array of string target_type=multigroup 時 LINE 群組 ID 陣列;不屬於此 Bot 或未啟用的群組會被略過
image_url string 否 圖片網址,必須是 https:// 開頭的公開網址(最長 1000 字元);與 image 擇一
video_url string 否 影片網址(https://,最長 1000 字元);與 video 擇一。帶影片時必須同時帶縮圖(preview_image_url 或 preview_image)
preview_image_url string 否 影片縮圖網址(https://,最長 1000 字元);與 preview_image 擇一
image file 否 僅 multipart;jpg/png,上限 10MB;與 image_url 擇一
video file 否 僅 multipart;mp4,上限 200MB;與 video_url 擇一,必須同時帶縮圖
preview_image file 否 僅 multipart;jpg/png,上限 1MB;與 preview_image_url 擇一
file file 否 僅 multipart;pdf/zip/docx/xlsx/csv/txt,上限 20MB。LINE 沒有檔案訊息類型:檔案存於私有儲存,訊息末尾附上下載連結
file_expires_days integer 否 下載連結有效天數,1–7,預設 3;只能搭配 file 使用
三種媒介:(1)媒體網址:image_url/video_url/preview_image_url,LINE 只接受公開 HTTPS;(2)媒體直接上傳:image/video/preview_image,系統先上傳成公開網址再發送;同一媒體的網址與上傳檔只能擇一,兩者都帶回 422;(3)檔案:file,以下載連結附在文字訊息末尾。不接受 send_type 參數,帶了回 422。
大小上限分三道:(1)程式上限 image 10MB、video 200MB、preview_image 1MB、file 20MB,超過回 422;(2)超過伺服器 php.ini 的 upload_max_filesize 回 422「檔案超過伺服器上傳上限」;(3)整個請求超過 php.ini 的 post_max_size 時,在進入本端點之前就回 413(不是 422)。實際可用上限取決於部署環境的 php.ini 與前端代理(例如 nginx 的 client_max_body_size),程式上限不保證等於實際上限。
target_type 說明
target_type收件對象搭配參數
direct指定 LINE 用戶direct_user_id
member單一會員(找不到或未啟用回 404)member_id
multicast多位會員,上限 500 位(一個都找不到回 404)member_ids
group單一群組(找不到或未啟用回 404)group_id
multigroup多個群組,逐一發送(一個都找不到回 404)group_ids
成功回應(帶 file)
{
    "status": 200,
    "message": "訊息發送成功",
    "data": {
        "file": {
            "id": 12,
            "url": "https://YOUR_DOMAIN/f/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "expires_at": "2026-10-23T12:34:56+08:00"
        }
    }
}

沒帶 file 且不是 multigroup 時,回應不含 data。data.file.id 可用 DELETE /api/sent-files/{id} 撤銷下載連結。

失敗回應(LINE 發送失敗,multigroup)
{
    "status": 502,
    "message": "訊息發送失敗",
    "error": {
        "reason": "line_rate_limited",
        "line_status": 429,
        "detail": "LINE API 回 429:本月訊息額度已用完或觸發 rate limit,額度重置或降速前重試無效"
    },
    "data": {
        "groups": {
            "succeeded": ["Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1"],
            "failed": [
                {"group_id": "Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2", "reason": "line_rate_limited", "line_status": 429}
            ],
            "skipped": []
        }
    }
}
error.reason(HTTP 502)
reason說明
line_rate_limitedLINE 回 429:本月訊息額度已用完或觸發 rate limit,額度重置或降速前重試無效
line_unauthorizedLINE 回 401:Channel Access Token 無效或已過期
line_rejectedLINE 回其他 4xx:請檢查收件 ID 與訊息內容是否符合 LINE 規格
line_server_errorLINE 回 5xx,可稍後重試
line_unreachable連線 LINE 失敗或逾時(此時 line_status 為 null)
line_failed發送失敗但沒有細節
time_budget_exceededmultigroup 超過時間預算,其餘群組未發送(列在 data.groups.skipped)

error.line_status 為 LINE 回的 HTTP 狀態碼。target_type=multigroup 時另回 data.groups:succeeded(已送達的群組 ID)、failed(group_id、reason、line_status)、skipped(未發送的群組 ID)。

重試不安全:multigroup 只要任一群組發送失敗,整筆就回 502,但其他群組可能已收到;已送達的群組不會回滾,直接重試會讓那些群組收到兩次。請依 data.groups.failed 與 skipped 只重送沒收到的群組。
其他錯誤狀態
狀態碼說明
401Token 無效或未提供
404Bot、會員或群組不存在,或不屬於此 Token 的擁有者
413請求超過 php.ini post_max_size(在本端點之前回應)
422參數錯誤,errors 會列出欄位與原因(含超過大小上限、message 超過 5000 字)
500檔案或媒體儲存失敗(訊息未發送),或伺服器內部錯誤(訊息不含內部細節)
範例
curl -X POST "http://YOUR_DOMAIN/api/messages/send" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "bot_id=6" \
  -F "message=出貨通知" \
  -F "target_type=group" \
  -F "group_id=Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "file=@報價單.pdf" \
  -F "file_expires_days=3"
撤銷發送檔案的下載連結

端點: DELETE /api/sent-files/{id}

描述: {id} 為發送時回應的 data.file.id。撤銷後下載連結回 410;重複撤銷視為成功。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
成功回應
{
    "status": 200,
    "message": "已撤銷",
    "data": {
        "id": 12,
        "revoked_at": "2026-10-20T12:34:56+08:00"
    }
}
失敗回應
狀態碼說明
401Token 無效或未提供
404檔案不存在或不屬於此 Token 的擁有者
500伺服器內部錯誤(訊息不含內部細節)
範例
curl -X DELETE "http://YOUR_DOMAIN/api/sent-files/12" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
下載發送的檔案

端點: GET /f/{token}

描述: 收件人點訊息中的下載連結即走這個端點。不需登入,連結中的 token 即憑證,請勿外流。HEAD 請求只回標頭,不計入下載次數。

回應
狀態碼內容說明
200檔案本體以附件下載(Content-Disposition: attachment,保留原始檔名)
404純文字「找不到檔案」連結不存在,或檔案已不在儲存體
410純文字「下載連結已失效」已撤銷、已過期,或所屬 Bot 已刪除
503純文字「檔案暫時無法下載,請稍後再試」儲存體暫時錯誤
查詢 LINE 本月訊息額度

端點: GET /api/bots/{id}/quota(單一 Bot)、GET /api/bots/quota(所有 Bot)

描述: 向 LINE 查詢本月訊息額度與用量,只能查此 Token 擁有者自己的 Bot。

請求標頭
Authorization: Bearer YOUR_API_TOKEN
成功回應(GET /api/bots/{id}/quota)
{
    "status": "success",
    "data": {
        "bot_id": 17,
        "bot_name": "NT-AI",
        "type": "limited",
        "unlimited": false,
        "limit": 3000,
        "usage": 199,
        "remaining": 2801
    }
}
成功回應(GET /api/bots/quota)
{
    "status": "success",
    "data": [
        {"bot_id": 17, "bot_name": "NT-AI", "type": "limited", "unlimited": false, "limit": 3000, "usage": 199, "remaining": 2801, "error": null},
        {"bot_id": 18, "bot_name": "客服", "type": "none", "unlimited": true, "limit": null, "usage": 52, "remaining": null, "error": null},
        {"bot_id": 19, "bot_name": "測試", "type": null, "unlimited": null, "limit": null, "usage": null, "remaining": null, "error": "LINE API 暫時無法取得額度資訊,請稍後再試"}
    ]
}
回應參數說明
參數名稱類型描述
typestringLINE 的額度類型;none 表示無上限方案
unlimitedbooleantrue 表示無上限方案(type=none)
limitinteger/null本月額度;無上限時為 null(不是 0)
usageinteger本月已用量
remaininginteger/null剩餘額度(不小於 0);無上限時為 null
errorstring/null僅 GET /api/bots/quota:該 Bot 查詢失敗時的原因,此時數字欄位與 type、unlimited 皆為 null
失敗回應(GET /api/bots/{id}/quota)
{
    "status": "error",
    "message": "LINE API 暫時無法取得額度資訊,請稍後再試"
}
狀態碼說明
400此 Bot 尚未設定 Channel Access Token
401Token 無效或未提供
404Bot 不存在或不屬於當前用戶
500伺服器內部錯誤(訊息不含內部細節)
502上游 LINE API 查詢失敗:LINE 401 時提示檢查 Token;429 為 rate limit;其他(5xx、逾時)請稍後再試

GET /api/bots/quota 中個別 Bot 查詢失敗不影響其他 Bot,該筆以 error 說明原因、整體仍回 200。整批有 30 秒時間預算,超過後其餘 Bot 不查詢並帶 error,請改用 GET /api/bots/{id}/quota 單獨查詢。

範例
curl -X GET "http://YOUR_DOMAIN/api/bots/17/quota" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Webhooks

本系統提供 Webhook URL,您可以通過 HTTP 請求直接發送訊息而無需使用 API Token。

使用 Webhook URL 發送通知

端點: POST /webhook/{botId}/{token}

描述: 使用 Webhook URL 直接發送通知訊息。請求 body 不含 events 陣列時視為發送請求(含 events 的是 LINE 平台事件)。

獲取方式: 在 Bot 管理頁面中可以看到每個 Bot 生成的唯一 Webhook URL(即上述路徑的完整網址,{botId} 與 {token} 已代入)。

新功能: 現在支援指定特定用戶 ID 發送訊息、群發訊息以及添加標題和圖片!
請求參數
參數名稱 類型 必填 描述
message string 是 要發送的訊息內容(最多 5000 字元)
title string 否 訊息標題(最多 255 字元),會顯示在訊息開頭
image_url string 否 要附加的圖片 URL(必須是公開可訪問的圖片)
video_url string 否 要附加的影片 URL(必須是 https:// 開頭、公開可訪問,最多 1000 字元)。需同時提供 preview_image_url,且必須指定發送目標(recipient_id/recipient_ids/recipient_group_id/recipient_group_ids),否則回傳 422
preview_image_url string 否 影片的預覽縮圖 URL(必須是 https:// 開頭,最多 1000 字元)。帶 video_url 時必填;單獨使用會回傳 422
recipient_id string 否 指定單一接收者的 LINE 用戶 ID
recipient_ids array 或 string 否 指定多個接收者的 LINE 用戶 ID 陣列,最多 500 個。也可以用逗號分隔的字串,例如: "id1,id2,id3"
send_to_all boolean 否 是否發送給所有會員(廣播,需謹慎使用)
recipient_group_id string 否 指定單一接收群組的 LINE 群組 ID
recipient_group_ids array 或 string 否 指定多個接收群組的 LINE 群組 ID 陣列。也可以用逗號分隔的字串,例如: "id1,id2,id3"
注意: recipient_id、recipient_ids、recipient_group_id、recipient_group_ids 和 send_to_all 參數不能同時使用。您只能選擇其中一種發送方式。如果都不指定,則使用預設接收者。
成功回應
{
    "status": "success",
    "message": "訊息發送成功",
    "data": {
        "recipients_count": 1,
        "sent_at": "2023-12-31T12:34:56"
    }
}
失敗回應
{
    "status": "error",
    "message": "錯誤訊息",
    "errors": {
        "message": ["訊息內容不能為空"]
    }
}
範例
# 基本用法
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{"message":"Hello World"}'

# 添加標題和圖片
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{
    "message":"這是訊息內容", 
    "title":"訊息標題", 
    "image_url":"https://example.com/image.jpg"
  }'

# 發送給特定用戶
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{
    "message":"這是發送給特定用戶的訊息", 
    "recipient_id":"U4af4980629..."
  }'

# 群發給多個用戶
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{
    "message":"這是群發訊息", 
    "recipient_ids":["U4af4980629...", "U8c6d6tx42b..."]
  }'

# 發送給特定群組
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{
    "message":"這是發送給特定群組的訊息", 
    "recipient_group_id":"C4af4980629..."
  }'

# 群發給多個群組
curl -X POST \
  "YOUR_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{
    "message":"這是群發給多個群組的訊息", 
    "recipient_group_ids":["C4af4980629...", "C8c6d6tx42b..."]
  }'
<?php
$webhookUrl = 'YOUR_WEBHOOK_URL';

// 基本用法
function sendWebhook($message, $extraParams = []) {
    global $webhookUrl;
    
    // 準備請求資料
    $data = array_merge(['message' => $message], $extraParams);
    
    // 初始化 cURL
    $ch = curl_init($webhookUrl);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    ]);

    // 執行請求
    $response = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return [
        'code' => $statusCode,
        'response' => json_decode($response, true)
    ];
}

// 發送基本訊息
$result = sendWebhook('Hello World');

// 添加標題和圖片
$result = sendWebhook('這是訊息內容', [
    'title' => '訊息標題',
    'image_url' => 'https://example.com/image.jpg'
]);

// 發送給特定用戶
$result = sendWebhook('這是發送給特定用戶的訊息', [
    'recipient_id' => 'U4af4980629...'
]);

// 群發給多個用戶
$result = sendWebhook('這是群發訊息', [
    'recipient_ids' => ['U4af4980629...', 'U8c6d6tx42b...']
]);

// 發送給特定群組
$result = sendWebhook('這是發送給特定群組的訊息', [
    'recipient_group_id' => 'C4af4980629...'
]);

// 群發給多個群組
$result = sendWebhook('這是群發給多個群組的訊息', [
    'recipient_group_ids' => ['C4af4980629...', 'C8c6d6tx42b...']
]);
const webhookUrl = 'YOUR_WEBHOOK_URL';

// 基本發送函數
async function sendWebhook(message, extraParams = {}) {
    try {
        const response = await fetch(webhookUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                message,
                ...extraParams
            })
        });
        
        return await response.json();
    } catch (error) {
        console.error('Error:', error);
        return { status: 'error', message: error.message };
    }
}

// 基本用法
sendWebhook('Hello World')
    .then(result => console.log(result));

// 添加標題和圖片
sendWebhook('這是訊息內容', {
    title: '訊息標題',
    image_url: 'https://example.com/image.jpg'
}).then(result => console.log(result));

// 發送給特定用戶
sendWebhook('這是發送給特定用戶的訊息', {
    recipient_id: 'U4af4980629...'
}).then(result => console.log(result));

// 群發給多個用戶
sendWebhook('這是群發訊息', {
    recipient_ids: ['U4af4980629...', 'U8c6d6tx42b...']
}).then(result => console.log(result));

// 發送給特定群組
sendWebhook('這是發送給特定群組的訊息', {
    recipient_group_id: 'C4af4980629...'
}).then(result => console.log(result));

// 群發給多個群組
sendWebhook('這是群發給多個群組的訊息', {
    recipient_group_ids: ['C4af4980629...', 'C8c6d6tx42b...']
}).then(result => console.log(result));
// Google Apps Script 範例

// 設定 Webhook URL
var WEBHOOK_URL = 'YOUR_WEBHOOK_URL';

/**
 * 基本發送函數
 * 
 * @param  {string} message 訊息內容
 * @param  {object} extraParams 額外參數
 * @return  {object} API 回應
 */
function sendWebhook(message, extraParams) {
  // 準備請求資料
  var payload = {
    'message': message
  };
  
  // 合併額外參數
  if (extraParams) {
    for (var key in extraParams) {
      payload[key] = extraParams[key];
    }
  }
  
  // 設定 API 請求選項
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload),
    'muteHttpExceptions': true
  };
  
  // 發送 API 請求
  var response = UrlFetchApp.fetch(WEBHOOK_URL, options);
  
  // 解析 API 回應
  var result = JSON.parse(response.getContentText());
  
  // 記錄結果
  Logger.log('發送結果: ' + result.message);
  
  return result;
}

/**
 * 發送基本訊息
 */
function sendBasicMessage() {
  sendWebhook('Hello World');
}

/**
 * 發送帶標題和圖片的訊息
 */
function sendRichMessage() {
  sendWebhook('這是訊息內容', {
    'title': '訊息標題',
    'image_url': 'https://example.com/image.jpg'
  });
}

/**
 * 發送訊息給特定用戶
 */
function sendToSpecificUser() {
  sendWebhook('這是發送給特定用戶的訊息', {
    'recipient_id': 'U4af4980629...'
  });
}

/**
 * 群發訊息給多個用戶
 */
function sendToMultipleUsers() {
  sendWebhook('這是群發訊息', {
    'recipient_ids': ['U4af4980629...', 'U8c6d6tx42b...']
  });
}

/**
 * 發送訊息給特定群組
 */
function sendToSpecificGroup() {
  sendWebhook('這是發送給特定群組的訊息', {
    'recipient_group_id': 'C4af4980629...'
  });
}

/**
 * 群發訊息給多個群組
 */
function sendToMultipleGroups() {
  sendWebhook('這是群發給多個群組的訊息', {
    'recipient_group_ids': ['C4af4980629...', 'C8c6d6tx42b...']
  });
}

/**
 * 與 Google Sheet 整合範例
 * 根據試算表內容發送訊息
 */
function sendMessagesFromSheet() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var dataRange = sheet.getRange("A2:C" + sheet.getLastRow());
  var data = dataRange.getValues();
  
  for (var i = 0; i < data.length; i++) {
    var row = data[i];
    var message = row[0];  // A列:訊息內容
    var userId = row[1];   // B列:LINE用戶ID
    var isSent = row[2];   // C列:是否已發送
    
    // 如果尚未發送
    if (!isSent) {
      // 發送訊息
      var result = sendWebhook(message, {
        'recipient_id': userId
      });
      
      // 如果發送成功,標記為已發送
      if (result.status === 'success') {
        sheet.getRange(i + 2, 3).setValue(true);
      }
    }
  }
}