教學

@line/bot-sdk Express 中介軟體實作範例(v11)

@line/bot-sdk v11 的 Express + TypeScript Webhook 完整範例:middleware() 該放哪裡、validateSignature 在 Node.js、Python、PHP 的用法、搭配 Flex Message 的 replyMessage、sentMessages 與引用 token,以及實際會遇到的錯誤。

LineBot.pro Team更新 14 分鐘閱讀

@line/bot-sdk Express 中介軟體實作範例(v11)
目錄
  1. Express 中介軟體做了什麼
  2. 安裝與設定(Node.js 22 以上)
  3. 最小可執行範例
  4. validateSignature 的運作方式(Node.js、Python、PHP)
  5. replyMessage(replyToken) 完整範例
  6. ReplyMessageResponse.sentMessages 與引用 token
  7. 實際會遇到的錯誤
  8. 正式環境的 Webhook 架構
  9. LineBot.pro 能派上用場的地方
  10. 常見問題

Express 中介軟體做了什麼

官方套件 @line/bot-sdk 的 middleware() 是一個相容 Express 的處理器,掛在接收 LINE Webhook 的路由上。它會讀取未經處理的請求內容(raw body),用你的 Channel secret 驗證 x-line-signature 標頭(HMAC-SHA256),簽章相符時再把 JSON 解析進 req.body,你的處理器就能直接使用 req.body.events。驗證失敗時,它只會把錯誤交給 next(),自己不會回應請求。

簽章驗證只對 LINE 原本送出的位元組有效。只要有東西先解析了請求內容,例如全域設定的 express.json(),驗證就會失敗。本文程式碼以 2026 年 10 月的最新版 SDK 11.3.0 為準,需要 Node.js 22 以上。第 11 版移除了舊的 Client 類別,所以使用 new Client(config) 和 client.replyMessage(token, message) 的舊範例已經無法編譯,本文改用 LineBotClient。

匯出項目用途
middleware({ channelSecret })讀取 raw body、驗證簽章,並把 JSON 解析進 req.body
LineBotClient.fromChannelAccessToken()具型別的 Messaging API 用戶端(回覆、推播、取得個人資料等)。messagingApi.MessagingApiClient 也仍然有匯出
webhook.CallbackRequest、webhook.EventWebhook 內容與各種事件的型別
validateSignature(body, secret, signature)以函式形式做同樣的驗證,適用於 Route Handler 與無伺服器環境
SignatureValidationFailed、JSONParseError中介軟體交給 next() 的錯誤

要開發對話本身,可以參考說明對話流程與 AI 回覆的 LINE Bot 開發教學;LINE Login 與不透過 SDK 呼叫 API 的做法,請看 LINE API 串接教學。

安裝與設定(Node.js 22 以上)

你需要一個 Messaging API 頻道。自 2024 年 9 月起,已經無法在 LINE Developers Console 直接建立這種頻道:請先建立 LINE 官方帳號,再到 LINE Official Account Manager 為它啟用 Messaging API(建立帳號的步驟可參考 LINE 官方帳號申請教學)。接著準備專案:

bash
mkdir line-webhook && cd line-webhook
npm init -y
npm pkg set type=module
npm install express @line/bot-sdk
npm install -D typescript tsx @types/express @types/node

在 LINE Developers Console 打開頻道,複製 Channel secret(Basic settings 分頁)與一組 Channel access token(Messaging API 分頁)。LINE 建議使用可自訂有效期限的 Channel access token v2.1;只想先跑起來的話,用長期(long-lived)token 最快。

bash
# .env(不要把這個檔案提交到版本控制)
LINE_CHANNEL_SECRET=your-channel-secret
LINE_CHANNEL_ACCESS_TOKEN=your-channel-access-token
PORT=3000

在 package.json 加入 dev 指令:

json
{
  "scripts": {
    "dev": "tsx watch --env-file=.env src/index.ts"
  }
}

最小可執行範例

以下是一個回聲機器人(把收到的文字原樣回傳)的完整 src/index.ts,已確認可在 @line/bot-sdk 11.3.0、Express 5 與 TypeScript strict 模式下編譯。

ts
// src/index.ts
import express from "express"
import {
  JSONParseError,
  LineBotClient,
  SignatureValidationFailed,
  middleware,
  webhook,
} from "@line/bot-sdk"

const channelSecret = process.env.LINE_CHANNEL_SECRET
const channelAccessToken = process.env.LINE_CHANNEL_ACCESS_TOKEN
if (!channelSecret || !channelAccessToken) {
  throw new Error("Set LINE_CHANNEL_SECRET and LINE_CHANNEL_ACCESS_TOKEN")
}

const client = LineBotClient.fromChannelAccessToken({ channelAccessToken })
const app = express()

// LINE 中介軟體掛在 Webhook 路由上,而且要在任何 JSON 解析器之前
app.post("/webhook", middleware({ channelSecret }), async (req, res) => {
  const { events } = req.body as webhook.CallbackRequest
  await Promise.all(events.map(handleEvent))
  res.sendStatus(200)
})

// 其他路由照常解析 JSON
app.use(express.json())

async function handleEvent(event: webhook.Event) {
  if (event.type !== "message" || event.message.type !== "text" || !event.replyToken) return

  const { sentMessages } = await client.replyMessage({
    replyToken: event.replyToken,
    messages: [{ type: "text", text: `Echo: ${event.message.text}` }],
  })
  console.log("sent message IDs:", sentMessages.map((m) => m.id))
}

// 中介軟體只會把錯誤交給 next(),不會自己回 401
app.use((err: unknown, _req: express.Request, res: express.Response, next: express.NextFunction) => {
  if (err instanceof SignatureValidationFailed) {
    res.status(401).send("Invalid signature")
    return
  }
  if (err instanceof JSONParseError) {
    res.status(400).send("Invalid JSON")
    return
  }
  next(err)
})

const port = Number(process.env.PORT ?? 3000)
app.listen(port, () => console.log(`LINE bot listening on :${port}`))

讓它正常運作的關鍵有四個:

  1. 中介軟體掛在 Webhook 路由上,排在任何 JSON 解析器之前。 express.json() 是在這個路由之後才註冊的,所以不會碰到 /webhook。
  2. res.sendStatus(200)。 LINE 要求每一次 Webhook 請求都收到 200,包括為了確認連線而送來、events 為空陣列的請求。
  3. 錯誤處理器。 沒有它的話,簽章錯誤只會變成 Express 預設的 500;有了它,你可以回 401 或 400,記錄檔也看得出原因。
  4. 檢查 replyToken。 在 v11 的型別中 replyToken 是選填的,回覆前要先確認。

測試時,用 ngrok 之類的通道工具以 HTTPS 開放 3000 埠,在 Messaging API 分頁的 Webhook URL 填入 https://<你的網域>/webhook,按 Verify,開啟 Use webhook,再傳訊息給你的帳號。LINE 只接受 HTTPS 的 Webhook URL,而且憑證必須由一般瀏覽器信任的憑證機構簽發,自簽憑證不能用。另外請在 LINE Official Account Manager 關閉加入好友的歡迎訊息與自動回應訊息(兩者預設都是開啟),否則使用者會同時收到 LINE 的自動回應和你的機器人回覆。

validateSignature 的運作方式(Node.js、Python、PHP)

LINE 會替每一次 Webhook 請求簽章:以你的 Channel secret 為金鑰,計算請求內容的 HMAC-SHA256,再把 Base64 編碼後的結果放在 x-line-signature 標頭中(Verify webhook signature)。SDK 的 validateSignature() 會重新計算這個值,並以固定時間的方式比對兩者。用純 Node.js 寫,大致如下:

ts
import { createHmac, timingSafeEqual } from "node:crypto"

// 用幾行程式碼說明 validateSignature() 做的事
export function isValidSignature(body: string | Buffer, channelSecret: string, signature: string): boolean {
  const expected = createHmac("sha256", channelSecret).update(body).digest()
  const received = Buffer.from(signature, "base64")
  return expected.length === received.length && timingSafeEqual(expected, received)
}

驗證失敗時,原因幾乎都在 LINE 文件列出的常見錯誤裡:

  • 驗證前就解析或重新序列化了請求內容(express.json() 先執行、JSON.stringify(req.body)、重新排版 JSON)。請使用原始的位元組。
  • 代理伺服器、負載平衡器或框架改動了內容或編碼。請維持 UTF-8。
  • 用了別的頻道的 secret,或有人在 Console 重新發行了 Channel secret。
  • 環境變數結尾多了換行或空白。
  • 用錯演算法,例如用 HMAC-SHA1 取代 HMAC-SHA256。

不要用 IP 白名單取代簽章驗證。LINE 不公開 Webhook 的來源 IP,並建議改用簽章驗證。

不用 Express:Next.js Route Handler 與無伺服器環境

沒有 Express 中介軟體鏈的環境,就自己對請求的原始文字呼叫 validateSignature():

ts
// app/api/line/webhook/route.ts (Next.js App Router)
import { LineBotClient, validateSignature, webhook } from "@line/bot-sdk"

const channelSecret = process.env.LINE_CHANNEL_SECRET ?? ""
const client = LineBotClient.fromChannelAccessToken({
  channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN ?? "",
})

export async function POST(request: Request) {
  const body = await request.text() // raw body:先驗證,再 JSON.parse
  const signature = request.headers.get("x-line-signature") ?? ""

  if (!validateSignature(body, channelSecret, signature)) {
    return new Response("Invalid signature", { status: 401 })
  }

  const { events } = JSON.parse(body) as webhook.CallbackRequest
  await Promise.all(
    events.map(async (event) => {
      if (event.type !== "message" || event.message.type !== "text" || !event.replyToken) return
      await client.replyMessage({
        replyToken: event.replyToken,
        messages: [{ type: "text", text: `Echo: ${event.message.text}` }],
      })
    }),
  )
  return new Response("OK")
}

有些平台(例如 Google Cloud Functions、Firebase)會在你的程式執行前先解析請求內容。中介軟體會優先使用 req.rawBody;如果你手上只有解析過的物件,重新序列化會改變位元組,就無法可靠地驗證簽章。

Python(line-bot-sdk v3)

Python 版 SDK(3.26.0,Python 3.10 以上)在 WebhookHandler.handle() 裡做同樣的驗證。請使用 linebot.v3 模組;舊的 from linebot import LineBotApi, WebhookHandler 已不建議使用。

python
import os

from flask import Flask, abort, request
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
    ApiClient,
    Configuration,
    MessagingApi,
    ReplyMessageRequest,
    TextMessage,
)
from linebot.v3.webhooks import MessageEvent, TextMessageContent

app = Flask(__name__)
configuration = Configuration(access_token=os.environ["LINE_CHANNEL_ACCESS_TOKEN"])
handler = WebhookHandler(os.environ["LINE_CHANNEL_SECRET"])


@app.post("/webhook")
def webhook():
    signature = request.headers.get("X-Line-Signature", "")
    body = request.get_data(as_text=True)  # 使用 raw body,不要用 request.json
    try:
        handler.handle(body, signature)  # 先驗證簽章,再分派給處理函式
    except InvalidSignatureError:
        abort(400)
    return "OK"


@handler.add(MessageEvent, message=TextMessageContent)
def handle_text(event):
    with ApiClient(configuration) as api_client:
        MessagingApi(api_client).reply_message(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=f"Echo: {event.message.text}")],
            )
        )

只想做驗證的話,linebot.v3 的 SignatureValidator(channel_secret).validate(body, signature) 會回傳 True 或 False。

PHP(line-bot-sdk-php)

目前的 PHP 版 SDK(v12,PHP 8.2 以上)把這個方法放在 LINE\Parser\SignatureValidator 的靜態方法。它回傳 true 或 false,簽章為空時會丟出 InvalidSignatureException:

php
<?php
require __DIR__ . '/vendor/autoload.php';

use LINE\Parser\Exception\InvalidSignatureException;
use LINE\Parser\SignatureValidator;

$body = file_get_contents('php://input'); // raw request body
$signature = $_SERVER['HTTP_X_LINE_SIGNATURE'] ?? '';
$channelSecret = (string) getenv('LINE_CHANNEL_SECRET');

try {
    $isValid = SignatureValidator::validateSignature($body, $channelSecret, $signature);
} catch (InvalidSignatureException $e) {
    $isValid = false; // 沒有簽章標頭
}

if (!$isValid) {
    http_response_code(400);
    exit;
}
// 簽章正確:解碼 $body 並處理事件

舊版 SDK 在 \LINE\LINEBot 類別上提供 $bot->validateSignature($body, $signature),所以很多教學還是這樣寫,但目前的版本已經沒有這個類別。想一次完成驗證與事件解析,可以用 EventRequestParser::parseEventRequest($body, $channelSecret, $signature)。

replyMessage(replyToken) 完整範例

可以回覆的 Webhook 事件都會帶有 replyToken。依照 Messaging API reference,規則如下:

  • reply token 只能用一次。
  • 必須在收到 Webhook 後一分鐘內使用。超過時間不保證有效,而且 LINE 表示這個時限可能變更,所以請盡快回覆。
  • 一次回覆最多 5 個訊息物件。
  • 回覆訊息不計入每月的訊息則數;推播(push)、multicast、broadcast 與 narrowcast 則會計入。

以下範例回覆一則文字訊息和一個 Flex Message 泡泡(bubble):

ts
import { LineBotClient, webhook } from "@line/bot-sdk"

const client = LineBotClient.fromChannelAccessToken({ channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN ?? "" })

export async function confirmBooking(event: webhook.MessageEvent) {
  if (!event.replyToken) return

  const { sentMessages } = await client.replyMessage({
    replyToken: event.replyToken,
    messages: [
      { type: "text", text: "謝謝!您的預約已確認。" },
      {
        type: "flex",
        altText: "預約已確認:星期六 14:00",
        contents: {
          type: "bubble",
          body: {
            type: "box",
            layout: "vertical",
            contents: [
              { type: "text", text: "預約已確認", weight: "bold", size: "lg" },
              { type: "text", text: "星期六 14:00", color: "#666666", wrap: true },
            ],
          },
          footer: {
            type: "box",
            layout: "vertical",
            contents: [
              {
                type: "button",
                style: "primary",
                action: { type: "uri", label: "查看預約", uri: "https://example.com/bookings/123" },
              },
            ],
          },
        },
      },
    ],
    notificationDisabled: false, // true = 傳送時不發推播通知
  })

  return sentMessages
}

設定 notificationDisabled: true 時,訊息送達時不會跳出推播通知。需要超過 5 則訊息,或超過一分鐘才能回覆時,請用 pushMessage 傳送其餘訊息(會計入則數)。像呼叫 AI 這類耗時的處理,可以用 client.showLoadingAnimation({ chatId: userId, loadingSeconds: 20 }) 在一對一聊天中顯示 5 到 60 秒的載入動畫。

ReplyMessageResponse.sentMessages 與引用 token

replyMessage() 會回傳 ReplyMessageResponse,其中的 sentMessages 陣列對應每一則送出的訊息,順序與你的 messages 陣列相同:

ts
type ReplyMessageResponse = {
  sentMessages: Array<{
    id: string          // 已送出訊息的 ID
    quoteToken?: string // 只有可以被引用的訊息類型才有
  }>
}

pushMessage() 回傳的格式相同。只有可以被引用的訊息才會帶 quoteToken(引用 token):文字(v1 與 v2)、貼圖、圖片、影片、樣板訊息與 Flex Message(後兩者被引用時只會顯示 altText)。引用 token 沒有有效期限,可以重複使用,但只能用在送出該訊息的聊天室裡(Get quote tokens)。

要引用訊息,就在文字訊息上設定 quoteToken。使用者傳來的文字訊息也有自己的 quoteToken,所以機器人可以現在引用顧客的訊息,之後再引用自己的確認訊息:

ts
import { randomUUID } from "node:crypto"
import { LineBotClient, webhook } from "@line/bot-sdk"

const client = LineBotClient.fromChannelAccessToken({ channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN ?? "" })

export async function confirmOrder(event: webhook.MessageEvent, userId: string) {
  if (event.message.type !== "text" || !event.replyToken) return

  // 在回覆中引用顧客的訊息
  const { sentMessages } = await client.replyMessage({
    replyToken: event.replyToken,
    messages: [{ type: "text", text: "訂單 #1042 已確認。", quoteToken: event.message.quoteToken }],
  })
  const orderQuoteToken = sentMessages[0]?.quoteToken // 和訂單資料一起儲存

  // 之後(例如出貨時):用推播訊息引用這則確認訊息
  await client.pushMessage(
    { to: userId, messages: [{ type: "text", text: "您的訂單已出貨。", quoteToken: orderQuoteToken }] },
    randomUUID(), // X-Line-Retry-Key:用同一個 key 重試不會重複傳送
  )
}

pushMessage() 的第二個參數是 X-Line-Retry-Key。如果相同 key 的請求已經被接受,LINE 會回 409 而不會重複傳送,所以逾時後重試是安全的。

實際會遇到的錯誤

症狀原因解法
每次 Webhook 都回 500,Console 的 Verify 也失敗SignatureValidationFailed 落到 Express 預設的錯誤處理器加上最小範例中的錯誤處理器,再修正根本原因
你的錯誤處理器回 401middleware() 之前內容已被解析、Channel secret 錯誤或被重新發行、代理伺服器改動了內容在 /webhook 上最先執行 middleware()、檢查 secret、保留 raw body
你的錯誤處理器回 400JSONParseError:內容不是有效的 JSON多半是自己手動送的測試請求;LINE 真正的 Webhook 一定是 JSON
API 回 400 Invalid reply tokentoken 已用過、太晚使用,或屬於其他頻道只回覆一次而且要快;晚送的訊息改用推播
API 回 400 The request body has X error(s)訊息物件無效:超過 5 則、空白文字、Flex 屬性錯誤等,details[].property 會指出哪個欄位修正該欄位;可用「Validate message objects」系列端點事先檢查
API 回 401沒有 Channel access token,或 token 已過期、被撤銷重新發行 token
API 回 429超過速率限制,或 You have reached your monthly limit.(免費或加購訊息額度用完)放慢速度,並確認方案的訊息則數
API 回 413請求超過 2 MB減少訊息數量或縮小內容

API 呼叫失敗時,SDK 會丟出帶有 status、statusText、headers 和 body 的 HTTPFetchError:

ts
import { HTTPFetchError, LineBotClient } from "@line/bot-sdk"

const client = LineBotClient.fromChannelAccessToken({ channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN ?? "" })

export async function safeReply(replyToken: string, text: string) {
  try {
    await client.replyMessage({ replyToken, messages: [{ type: "text", text }] })
  } catch (err) {
    if (err instanceof HTTPFetchError) {
      // 例如 400 {"message":"Invalid reply token"} 或 429 {"message":"You have reached your monthly limit."}
      console.error("LINE API error", err.status, err.body)
      return
    }
    throw err
  }
}

這裡要更正一個常見的說法:LINE 預設不會重送失敗的 Webhook。**Webhook redelivery(重新傳送)**是 Messaging API 分頁中需要自行開啟的選項;開啟後,沒有收到 2xx 回應的 Webhook 會被重新傳送,次數與間隔 LINE 並未公開(Receive messages)。

正式環境的 Webhook 架構

圖表
使用者傳送訊息
      │
      ▼
LINE 平台 ── HTTPS POST + x-line-signature ──▶ POST /webhook(你的伺服器)
                                               │ middleware(): raw body → 驗證 HMAC → JSON
                                               │ 立即回 200 OK
                                               ▼
                                         handleEvent(event)
                                               │ 你的商業邏輯、資料庫、AI
                                               ▼
LINE 平台 ◀── replyMessage / pushMessage ──────┘
      │
      ▼
使用者看到回覆

LINE 建議以非同步方式處理事件,避免一個慢請求拖住後面的事件。常見做法是先回 200,再於背景處理事件,並用 webhookEventId 略過重複事件:

ts
const seen = new Set<string>() // 正式環境請存在 Redis 或資料庫

app.post("/webhook", middleware({ channelSecret }), (req, res) => {
  const { events } = req.body as webhook.CallbackRequest
  res.sendStatus(200) // 先回應:LINE 只需要 200

  for (const event of events) {
    if (seen.has(event.webhookEventId)) continue // 重複或重新傳送的事件
    seen.add(event.webhookEventId)
    handleEvent(event).catch((err) => console.error("event failed", event.webhookEventId, err))
  }
})

請留意:

  • 重複事件會發生。 網路問題或重新傳送都可能讓同一事件送達不只一次。webhookEventId 不會變,deliveryContext.isRedelivery 則告訴你是否為重送。執行多個實例時,請把處理過的 ID 存在 Redis 或資料庫,而不是記憶體。
  • 先回應也有代價。 回了 200 之後,LINE 不會再重送這個事件,所以失敗要自己記錄並重試。
  • 無伺服器平台可能在回應後凍結。 在 Vercel、AWS Lambda 等平台上,回應之後才執行的工作可能做不完。請在回應前完成處理(仍要在 reply token 的一分鐘內),或交給回應後執行的 API(例如 Next.js 的 after())或佇列。
  • 一個頻道只有一個 Webhook URL。 Messaging API 頻道會把所有事件送到同一個 URL,所以請在同一個服務裡依 event.type 分流,而不是讓多個服務共用同一個頻道。
  • 狀態由你保存。 LINE 不會替你保存對話狀態,請以使用者 ID 為鍵自行儲存。需要隨時可見的導覽時,請用圖文選單。

LineBot.pro 能派上用場的地方

LineBot.pro 不是 SDK、不是 API,也不是代管平台,而且沒有公開 API。不過在用上面的程式碼開發時,有兩個部分用得上:

  • 設計訊息與選單。 AI 產生器可以做出圖文訊息(一個含主圖、文字和按鈕的 Flex 泡泡)或圖文選單(圖片加 JSON,1 到 6 個按鈕)。你可以複製 JSON,或連同圖片一起下載,再用 replyMessage、pushMessage 傳送,或自行上傳選單。圖文訊息每次 8 點、圖文選單 10 點;新註冊送 50 點,之後可單次購買點數包,沒有月費(價格)。試用圖文訊息產生器
  • 完全不寫程式。 如果只需要依關鍵字回覆,LINE 關鍵字回覆機器人會用你核准過的圖文訊息卡片回覆一對一聊天。沒有對應關鍵字的訊息,可以選擇開啟 AI 分流來挑選卡片:AI 只負責選卡片,不會自己寫回答。這個機器人的運作方式是把 LineBot.pro 的 Webhook URL 填進你的頻道,所以無法和你自己的 Webhook 伺服器在同一個頻道並用。

若要串接預約系統、CRM 或 LIFF 應用程式,我們也承接 LINE 客製化開發。

常見問題

Q: 在 Express 裡,@line/bot-sdk 的中介軟體要放在哪裡? 放在 Webhook 路由上,而且要在任何內容解析器之前:app.post("/webhook", middleware({ channelSecret }), handler)。express.json() 請在這個路由之後再註冊,或只設定在需要的路由。全域解析器先執行的話,raw body 就沒了,每個請求都會以 SignatureValidationFailed 失敗。

Q: 為什麼我的 LINE Webhook 回 500 而不是 401? 中介軟體在驗證失敗時不會自己回應,而是呼叫 next(err)。沒有錯誤處理器時,Express 會把它變成 500。請加上一個處理器:遇到 SignatureValidationFailed 回 401,遇到 JSONParseError 回 400。

Q: 用 line-bot-sdk-php 要怎麼驗證簽章? 用從 php://input 讀到的 raw body 呼叫 LINE\Parser\SignatureValidator::validateSignature($body, $channelSecret, $signature)。它回傳布林值,簽章為空時丟出 InvalidSignatureException。\LINE\LINEBot 類別上的 $bot->validateSignature() 是舊版 SDK 的方法。

Q: 在 Python 要怎麼驗證簽章? line-bot-sdk v3 中,WebhookHandler(channel_secret).handle(body, signature) 會驗證並分派事件,不相符時丟出 InvalidSignatureError。只想驗證的話,用 linebot.v3 的 SignatureValidator(channel_secret).validate(body, signature)。傳入的必須是 raw body,在 Flask 中是 request.get_data(as_text=True)。

Q: ReplyMessageResponse.sentMessages 裡有什麼? 每則送出的訊息對應一個物件,順序與請求相同,內含訊息 id;可以被引用的訊息類型還會有 quoteToken。如果之後的訊息要引用它,請把引用 token 存起來。

Q: replyMessage 和 pushMessage 可以一起用嗎? 可以。先用 reply token 回覆(免費、約一分鐘內、最多 5 則),之後的訊息再用 pushMessage 傳送。推播訊息會計入方案的每月則數,每位收件者算 1 則。


下一步:

在你的官方帳號試試 LineBot.pro

製作符合 LINE 規格尺寸的圖文選單、圖文訊息與橫幅,發送群發訊息,並設定關鍵字自動回覆。

免費開始,送 50 點

免綁卡,點數永不過期。

想請專家幫你檢查帳號? LINE官方帳號健檢 ฿2,500

關於 LineBot.pro

LineBot.pro 是專為 LINE 官方帳號打造的 AI 工具,可製作圖文選單、圖文訊息與橫幅,群發行銷活動,並依關鍵字自動回覆。這些指南由我們的團隊根據 LINE Messaging API 的實務經驗撰寫。